설정 파일 경로와 참조 관계부터 확인하기
설정의 핵심 관계는 규칙이 정책 그룹을 선택하고, 정책 그룹이 노드를 선택하며, DNS가 도메인 조회 결과를 제공하는 것입니다. 이 관계에 따라 항목을 나누어 보면 구독 파일을 한 줄씩 수정하는 것보다 문제를 쉽게 찾을 수 있습니다. 이 글은 mihomo를 대상으로 합니다. mihomo는 흔히 Clash Meta 코어의 후속 프로젝트로 불립니다. 구형 Clash 코어와 그래픽 클라이언트마다 설정 병합 방식이 다르므로 같은 항목을 지원한다고 단정할 수 없습니다.
코어를 직접 실행한다면 작업 디렉터리를 ~/.config/mihomo로 지정하고, 해당 디렉터리에 설정 파일 config.yaml을 두면 됩니다. 클라이언트를 사용한다면 먼저 클라이언트가 실제로 코어에 전달하는 설정 경로를 확인하세요. 구독 원본, 오버라이드 파일, 실행 중인 설정은 서로 다른 파일일 수 있습니다. 실행 중인 설정 파일을 직접 편집하면 다음 구독 업데이트 때 덮어써질 수 있습니다.
mkdir -p ~/.config/mihomo
mihomo -v
mihomo -t -d ~/.config/mihomo
-v는 실행 중인 코어 버전을 확인하고, -t는 지정한 디렉터리의 설정을 검사합니다. 아직 설정 파일을 만들지 않았다면 마지막 명령에서 파일을 찾을 수 없다는 오류가 나오는 것은 정상입니다. 아래 앞의 여섯 절에 있는 YAML 조각을 순서대로 합치면 기본 설정을 만들 수 있습니다. 단, 예제의 로컬 SOCKS 노드는 특정 조건이 필요한 설정이며, 바로 사용할 수 있는 원격 프록시 서비스가 아닙니다.
1. mixed-port: 로컬 프록시 진입점 설정
mixed-port: 7890
allow-lan: false
bind-address: "127.0.0.1"
ipv6: false
mixed-port는 HTTP 프록시와 SOCKS5 프록시 연결을 모두 받으므로 브라우저와 명령줄 도구에서 127.0.0.1:7890을 함께 사용할 수 있습니다. 이 포트는 관리 패널용 포트도, 노드 서버의 원격 포트도 아닙니다. 로컬 앱에서만 사용할 경우 allow-lan: false를 유지하세요. 웹 문제를 확인한다는 이유로 수신 주소를 모든 네트워크 인터페이스로 변경하지 마세요.
| 항목 | 용도 | 이 글의 설정값 |
|---|---|---|
mixed-port | 앱이 연결하는 HTTP / SOCKS 진입점 | 7890 |
port | 별도의 HTTP 프록시 진입점 | 별도로 열지 않음 |
socks-port | 별도의 SOCKS 프록시 진입점 | 별도로 열지 않음 |
dns.listen | 코어 DNS 서비스의 수신 주소 | 127.0.0.1:1053 |
external-controller | 관리 API용이며 일반 웹 프록시 요청은 받지 않음 | 기본 설정에서는 생략 |
여기서는 최초 설정을 단순하게 하기 위해 IPv6를 일단 끕니다. 모든 네트워크에서 IPv6를 계속 꺼 두어야 한다는 뜻은 아닙니다. 나중에 IPv6를 활성화할 때는 DNS의 AAAA 응답, 노드의 IPv6 연결 상태, 시스템 라우팅을 함께 확인해야 합니다. LAN 기기와 공유하려면 하나의 불리언 값만 바꾸지 말고 수신 주소, 인증, 방화벽을 별도로 계획하세요.
ss -lntp 'sport = :7890'
curl --proxy http://127.0.0.1:7890 --connect-timeout 10 -I https://example.com/
첫 번째 명령으로 포트가 수신 중인지 확인합니다. 두 번째 명령은 데스크톱 환경의 시스템 프록시 설정을 우회해 프록시를 명시적으로 지정합니다. 코어가 실행 중일 때만 이 두 가지 검사가 의미가 있습니다. 연결이 거부되면 DNS를 바로 변경하기보다 먼저 프로세스와 포트를 확인하세요.
2. mode와 로그: 트래픽 분기 방식 고정하기
mode: rule
log-level: info
mode: rule은 rules 목록에 따라 출구를 선택한다는 뜻입니다. direct 모드는 직접 연결을 우선하고, global 모드는 전역 정책을 사용합니다. 규칙 문제를 확인할 때는 클라이언트에서 다른 모드로 바뀌지 않았는지 먼저 살펴보세요. 관리 API가 설정의 모드를 변경할 수도 있으므로 파일에 rule이라고 적혀 있다고 해서 현재 실행 상태도 반드시 같은 것은 아닙니다.
log-level: info는 일반 연결과 시작 오류를 확인하기에 적합합니다. 특정 규칙이나 DNS 동작을 추적해야 할 때는 잠시 debug로 바꾸고, 재현한 뒤 원래 설정으로 되돌리세요. 로그에는 접속한 도메인, 노드 주소, 연결 정보가 포함될 수 있으므로 공유하기 전에 민감한 내용을 삭제해야 합니다. 모드, 노드, DNS를 한꺼번에 바꾸지 마세요. 한 번에 한 묶음의 항목만 수정해야 변경 사항과 현상을 연결해 확인할 수 있습니다.
3. DNS: 업스트림, 수신 포트, fake-ip 구분하기
dns:
enable: true
listen: "127.0.0.1:1053"
ipv6: false
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
fake-ip-filter:
- "*.lan"
- "localhost"
default-nameserver:
- 223.5.5.5
- 119.29.29.29
nameserver:
- https://dns.alidns.com/dns-query
- https://doh.pub/dns-query
각 항목의 역할
listen은 로컬 DNS 서비스가 쿼리를 받을 주소를 지정합니다. 여기서는 권한이 필요하지 않은 포트1053을 사용해 기본 설정 단계에서 시스템이 흔히 쓰는53포트를 점유하지 않도록 합니다.default-nameserver는 DNS 업스트림 서버 자체의 도메인을 조회하는 데 사용합니다. 업스트림 주소를 먼저 해석하지 못해 이후 조회가 중단되는 상황을 방지합니다.nameserver는 기본 도메인 조회에 사용할 업스트림입니다. 예제에는 DoH 주소 두 개가 들어 있지만 실제 네트워크에서 해당 주소에 접속할 수 있어야 합니다. HTTPS 주소라고 해서 자동으로 연결이 보장되는 것은 아닙니다.
fake-ip는 조건에 맞는 도메인에 가상 주소를 응답한 뒤, 코어가 매핑 정보를 통해 도메인을 복원하고 연결을 처리하는 방식입니다. 원격 노드의 실제 IP가 아니며 대상 웹사이트가 198.18.0.0/16에 있다는 뜻도 아닙니다. 실제 DNS 응답이 필요한 LAN 서비스는 도메인을 fake-ip-filter에 추가해 fake-ip 응답을 사용하지 않도록 할 수 있습니다.
dig @127.0.0.1 -p 1053 example.com A
dig @127.0.0.1 -p 1053 localhost A
예제 설정에서는 첫 번째 쿼리가 보통 fake-ip 주소를 반환합니다. 필터 항목의 동작은 실제 DNS 응답에 따라 달라집니다. 시스템에 dig가 없다면 배포판에서 제공하는 DNS 도구 패키지를 설치할 수 있습니다. 코어의 수신 포트에 직접 쿼리하는 이유는 업스트림 조회 문제와 시스템이 코어 DNS를 사용하지 않는 문제를 구분하기 위해서입니다.
4. proxies: 노드 선언하기, 진입 포트를 업스트림으로 착각하지 않기
proxies:
- name: "로컬 SOCKS"
type: socks5
server: 127.0.0.1
port: 1080
udp: false
이 예제는 인증 없이 실행 중인 로컬 SOCKS5 서비스 127.0.0.1:1080을 참조합니다. 설정 간 참조 관계를 확인하는 데 쓸 수 있지만, 이 설정만으로 해당 서비스가 생성되지는 않습니다. 사용할 업스트림이 없다면 뒤에서 정책 그룹을 DIRECT로 선택해 로컬 수신과 규칙 로딩을 확인할 수는 있지만, 프록시 노드 연결까지 검증할 수는 없습니다.
name은 설정 내부에서 참조하는 이름이므로 뒤에 나오는 정책 그룹에서도 철자가 정확히 일치해야 합니다. 노드의 연결 주소는 server와 port에 지정합니다. 구독을 사용 중이라면 서비스 제공자가 실제로 안내한 노드 매개변수를 사용하세요. SOCKS5 예제를 다른 프로토콜로 기계적으로 바꾸면 안 됩니다. Shadowsocks, VMess, VLESS 등은 인증 및 전송 항목이 서로 다르며, 차이는 type에만 국한되지 않습니다.
5. proxy-groups: 규칙에서 안정적인 출구 이름 참조하기
proxy-groups:
- name: "출구"
type: select
proxies:
- DIRECT
- "로컬 SOCKS"
select는 수동 선택 그룹입니다. 규칙이 “출구”만 참조하면 노드를 바꿀 때 규칙을 하나씩 수정하지 않아도 됩니다. DIRECT는 내장된 직접 연결 정책이므로 proxies에 다시 선언할 필요가 없습니다. 반면 “로컬 SOCKS”는 앞 절에서 선언한 노드 이름과 일치해야 합니다. 선택 상태가 저장되지 않은 최초 실행을 고려해 이 예제에서는 기본 연결을 확인하기 쉽도록 DIRECT를 첫 항목에 둡니다.
그래픽 클라이언트는 보통 정책 그룹의 선택 상태를 저장합니다. 목록 순서를 바꿔도 이전 선택이 복원될 수 있으므로 테스트 전에 현재 선택된 항목을 확인하세요. 관리 API를 사용하지 않고 코어만 실행한다면 처음에는 첫 항목을 바꾼 뒤 재시작해 테스트할 수 있습니다. 이후 필요에 따라 로컬 제어 패널을 연결하세요.
자동 속도 측정이 필요할 때 url-test 추가하기
url-test는 지정한 URL에 주기적으로 요청을 보내 지연 시간을 비교합니다. 모든 웹사이트에 접속할 수 있음을 보장하는 기능은 아닙니다. 예를 들어 interval: 300은 300초마다 확인한다는 뜻이지, 요청할 때마다 속도를 측정한다는 뜻은 아닙니다. 노드가 적고 문제를 처음 확인하는 단계라면 먼저 select로 사용할 수 있는 연결을 찾고, 이후 자동 선택을 고려하세요. 그래야 상태 확인 실패와 노드 인증 실패를 혼동하지 않습니다.
6. rules: 구체적인 규칙부터 최종 기본 규칙까지
rules:
- DOMAIN-SUFFIX,lan,DIRECT
- DOMAIN,localhost,DIRECT
- IP-CIDR,127.0.0.0/8,DIRECT,no-resolve
- IP-CIDR,10.0.0.0/8,DIRECT,no-resolve
- IP-CIDR,172.16.0.0/12,DIRECT,no-resolve
- IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
- DOMAIN-SUFFIX,github.com,출구
- MATCH,출구
규칙은 위에서 아래로 검사하며, 하나가 일치하면 보통 이후 규칙은 확인하지 않습니다.DOMAIN은 전체 도메인과 일치하고, DOMAIN-SUFFIX는 해당 도메인과 하위 도메인에 일치합니다. 예를 들어 github.com과 api.github.com은 모두 예제의 접미사 규칙에 일치하지만, GitHub 관련 리소스 도메인이 전부 포함된다는 뜻은 아닙니다.
IP-CIDR은 대상 IPv4 주소 대역과 일치합니다. no-resolve는 이 IP 규칙을 판단하기 위해 아직 해석되지 않은 대상 도메인을 임의로 조회하지 말라는 뜻입니다. 대상 IP가 이미 있으면 여전히 규칙과 비교할 수 있습니다. 따라서 LAN 서비스는 필요한 도메인 규칙도 함께 관리해야 하며, 사설 주소 대역만으로 모든 로컬 이름을 추정할 수는 없습니다.
MATCH는 마지막에 적용되는 기본 규칙이므로 목록의 맨 끝에 둬야 합니다. 앞에 쓰면 이후 규칙이 매칭될 기회가 사라집니다. 이 예제는 기본 설정 단계에서 GeoIP 데이터베이스에 의존하지 않도록 사설 주소 대역을 명시적으로 사용합니다. 모든 특수 용도 주소가 포함된 전체 목록은 아닙니다. 앞의 기본 설정에서는 IPv6를 꺼 두었지만, IPv6를 활성화한다면 관련 주소 규칙도 추가해야 합니다.
여기까지의 앞 여섯 절을 합치면 기본 설정이 완성됩니다. 목적은 항목 간 관계를 보여주는 것이며, 지역별 트래픽 분기 설정 전체를 제공하는 것은 아닙니다. 세분화된 규칙 세트, DNS 분기, 프로세스 규칙은 기본 설정이 정상 작동한 뒤 하나씩 추가하세요. 관련 항목은 설정 레퍼런스에서 계속 확인할 수 있습니다.
7. proxy-providers: 노드가 많을 때 파일 분리하기
노드가 늘어나면 노드 목록을 Provider 파일로 분리할 수 있습니다. 아래는 4절과 5절을 대체하는 선택 사항입니다. 앞에서 만든 것과 이름이 같은 정책 그룹을 중복해서 붙여 넣지 마세요. 기본 설정은 파일에서 노드를 읽고, 정책 그룹은 use로 Provider 이름을 참조합니다. Provider 이름을 노드 목록에 넣는 방식이 아닙니다.
proxy-providers:
local-pool:
type: file
path: ./providers/local.yaml
health-check:
enable: true
url: https://www.gstatic.com/generate_204
interval: 600
proxy-groups:
- name: "출구"
type: select
proxies:
- DIRECT
use:
- local-pool
~/.config/mihomo/providers/local.yaml에 아래 내용을 작성하고, 먼저 providers 디렉터리를 만드세요. 상대 경로는 실제 작업 디렉터리를 기준으로 확인해야 합니다. 이 글의 실행 명령은 -d로 디렉터리를 명시합니다.
proxies:
- name: "로컬 SOCKS"
type: socks5
server: 127.0.0.1
port: 1080
udp: false
Provider 파일은 노드 모음 형식에 맞아야 합니다. dns, rules 같은 항목이 포함된 전체 클라이언트 설정 파일의 이름만 바꿔 사용하는 방식은 올바르지 않습니다. type: http를 사용한다면 실제 구독 URL을 입력하고 Provider 수준의 interval로 갱신 주기를 설정해야 합니다. 구독 주소에는 보통 접속 인증 정보가 포함되므로 공개 로그나 스크린샷에 노출하지 마세요.
두 주기를 구분하세요. Provider의 interval은 원격 콘텐츠 갱신 주기를 설정하고, health-check.interval은 노드 검사 주기를 설정합니다. 여기서는 로컬 파일 Provider를 사용하므로 상태 검사를 600초마다 실행해도 원격에서 새 노드를 내려받지 않습니다. 검사 대상 주소에 접속할 수 없으면 그룹 내 모든 노드의 상태 확인이 실패할 수도 있으므로 실제 요청 결과와 함께 판단하세요.
8. TUN은 추가 트래픽 처리 계층이며 필수 설정이 아닙니다
명시적 프록시가 정상 작동하는지 먼저 확인한 다음 TUN을 활성화하세요. TUN은 가상 네트워크 장치와 라우팅을 통해 트래픽을 처리합니다. 일반적인 mixed-port만으로 모든 앱이 자동으로 프록시를 사용하는 것은 아닙니다. 아래 설정은 권한과 라우팅 환경을 준비한 Linux 테스트에만 해당하며, 앞의 여섯 절에 포함된 기본 설정이 아닙니다.
tun:
enable: true
stack: mixed
auto-route: true
auto-detect-interface: true
dns-hijack:
- any:53
auto-route는 코어에 필요한 라우팅 설정을 요청하고, auto-detect-interface는 출구 인터페이스를 찾는 데 사용합니다. TUN 장치 생성과 라우팅 변경에는 충분한 시스템 권한이 필요합니다. 보통 클라이언트의 서비스 모드나 관리자가 설정한 서비스가 권한을 제공하며, 일반 사용자 프로세스에는 필요한 권한이 없을 수 있습니다. dns-hijack은 해당 처리 경로로 들어오는 기존 53번 포트 DNS 요청을 가로채는 기능이며, 브라우저의 DoH 트래픽을 직접 복호화하지는 않습니다.
9. 문법, 수신 포트, DNS, 출구 순서로 확인하기
- 현재 파일을 백업하세요. 수정하기 전에 정상 작동이 확인된 설정을 보관하세요. 특히 구독 오버라이드와 직접 작성한 규칙은 반드시 백업해야 합니다. 백업 파일을 현재 사용 중인 설정 파일로 착각하지 않도록 주의하세요.
- 문법과 참조 관계를 확인하세요.
mihomo -t -d ~/.config/mihomo를 실행합니다. 노드 이름이 없거나 정책 그룹 참조가 잘못되었거나 YAML 들여쓰기가 틀린 경우에는 재시작하기 전에 해결해야 합니다. - 테스트 인스턴스는 하나만 실행하세요. 그래픽 클라이언트가 이미
7890을 사용 중이면 해당 코어를 먼저 중지하거나 테스트 설정에 다른 포트를 지정하세요. 포트 충돌은 노드 문제가 아닙니다. - 포그라운드에서 실행해 로그를 확인하세요. 아래 실행 명령을 사용한 뒤 다른 터미널에서 포트, DNS, 프록시 요청을 테스트합니다. 포그라운드 테스트를 종료하려면 Ctrl+C를 누르세요.
mihomo -d ~/.config/mihomo
# 다른 터미널에서 실행
ss -lntp 'sport = :7890'
dig @127.0.0.1 -p 1053 example.com A
curl --proxy http://127.0.0.1:7890 --connect-timeout 10 -I https://example.com/
curl --proxy socks5h://127.0.0.1:7890 --connect-timeout 10 -I https://example.com/
socks5h의 h는 대상 도메인 처리를 프록시 쪽에 맡긴다는 뜻입니다. 클라이언트가 먼저 로컬에서 도메인을 조회하는 방식과 다릅니다. 이를 통해 앱 자체의 DNS 처리 경로에서 문제가 발생했는지 확인할 수 있습니다. HTTP 요청에서 오류 상태가 반환되더라도 대상 웹사이트의 응답인지 프록시 연결 실패인지 구분해야 합니다. 응답이 200인지 여부만으로 네트워크 연결 상태를 판단하지 마세요.
설정 테스트를 통과했다는 것은 설정 파일을 파싱하고 불러올 수 있다는 뜻일 뿐, 구독 주소 접속, 노드 인증, DNS 업스트림 연결, TUN 라우팅까지 정상이라는 보장은 아닙니다. 기본 검증을 마친 뒤 systemd 서비스나 클라이언트 관리 기능을 다시 활성화하고, 서비스에서 사용하는 바이너리, 작업 디렉터리, 설정 파일이 테스트 때와 같은지 확인하세요. 배포 방법은 빠른 시작을 참고하고, 연결 문제가 있다면 문제 해결부터 살펴보세요.
최종적으로 앱이 mixed-port에 연결되고, 규칙이 “출구”를 선택하며, 정책 그룹이 사용 가능한 노드나 DIRECT를 선택하고, DNS가 필요한 조회 결과를 제공하는 명확한 연결 구조를 유지하세요. 이후 구독 갱신, 규칙 세트, TUN을 추가할 때는 한 번에 한 계층씩 확장하고 같은 검증 명령으로 다시 확인하세요.