1. 설정 파일 열기: 먼저 백업하고 실제 적용 파일 확인하기

클라이언트의 설정 목록에서 현재 사용 중인 설정을 선택하고 편집 또는 파일 열기 메뉴를 찾으세요. 먼저 복사본을 저장한 뒤 YAML을 수정합니다. 클라이언트마다 메뉴 이름은 다를 수 있습니다. 중요한 것은 현재 활성화된 설정을 편집하는지, 구독 원본이나 로컬 오버라이드 또는 클라이언트가 생성한 런타임 파일을 편집하는지 확인하는 것입니다. 수정 후에는 설정을 다시 불러오고 로그에서 정상적으로 로드됐는지 확인하세요.

config.yaml은 흔히 쓰이는 파일명일 뿐, 반드시 이 이름이어야 하는 것은 아닙니다. 코어는 지정된 경로의 YAML을 읽을 수 있으며, GUI 클라이언트는 구독마다 별도 파일을 저장하기도 합니다. 구독을 업데이트하면 원본 파일이 다시 다운로드되는 경우가 많습니다. 포트, DNS, 사용자 지정 규칙을 계속 유지하려면 클라이언트의 오버라이드 또는 병합 기능을 이용하는 것이 좋습니다. 병합 방식은 해당 클라이언트의 실제 구현을 따르세요.

YAML 기본 점검 3가지

  • 들여쓰기는 공백으로: 단계마다 공백 2칸을 권장하며 Tab은 사용하지 마세요. 최상위 dns, proxies, rules는 같은 위치에 두고 하위 항목은 들여씁니다.
  • 목록 항목 앞에는 하이픈:rules의 각 규칙은 목록 항목입니다. proxy-groups의 각 정책 그룹도 목록 항목으로 작성합니다.
  • 특수 문자열은 따옴표로: 비밀번호나 콜론 또는 샵이 포함된 문자열은 따옴표로 감싸는 것이 좋습니다. 같은 이름의 최상위 키를 중복 정의하지 말고, 영문 콜론 대신 전각 콜론을 쓰지 마세요.

설정은 입구에서 출구 순으로 살펴보면 이해하기 쉽습니다. 포트는 앱이 코어에 연결을 전달하는 방식을 정하고, DNS는 도메인과 관련 정보를 해석하며, 노드와 정책 그룹은 연결 출구를 제공합니다. 규칙은 각 연결이 어느 출구를 사용할지 결정합니다. 아래 내용도 이 순서로 설명하지만, YAML 최상위 필드의 배치 순서가 실행 순서를 결정하지는 않습니다.

2. 공통 설정: mixed-port, mode, 로그 수준

mixed-port: 7890
allow-lan: false
mode: rule
log-level: info
ipv6: false

이 설정은 HTTP와 SOCKS 프록시 요청을 7890 포트로 받고, 규칙 모드를 사용하며, 일반 실행 정보를 로그에 기록합니다. 포트 번호는 설명을 위한 예시이며 모든 클라이언트의 기본값은 아닙니다. 클라이언트 설정 화면에서 포트를 덮어쓰는 경우 실제로 열려 있는 포트를 기준으로 확인하세요.

필드기능 및 확인 사항
mixed-port하나의 포트에서 HTTP와 SOCKS 프록시 요청을 모두 받습니다. 앱에는 실제로 열려 있는 주소와 포트를 입력해야 합니다.
port / socks-portHTTP와 SOCKS 포트를 각각 제공합니다. 혼합 포트를 사용한다면 같은 용도로 별도 포트를 추가로 열 필요가 없습니다.
allow-lanLAN 기기가 프록시 포트에 접속할 수 있는지 설정합니다. 이 컴퓨터에서만 사용할 경우 우선 false로 두세요.
moderule은 규칙에 따라 트래픽을 분기하고, global은 전역 정책을 사용하며, direct는 프록시 없이 직접 연결합니다.
log-level문제 해결 중에는 일시적으로 debug로 바꿀 수 있습니다. 확인이 끝나면 info로 되돌려 상세 로그가 계속 쌓이지 않도록 하세요.
ipv6코어의 IPv6 사용 여부에 영향을 줍니다. DNS 모듈에도 별도의 IPv6 설정이 있으므로 이 항목만 확인해서는 안 됩니다.

포트가 열려 있어도 시스템 프록시가 활성화된 것은 아닙니다

mixed-port: 7890을 설정하면 코어가 프록시 연결을 받을 포트를 엽니다. macOS 앱의 트래픽을 시스템 프록시로 보내려면 클라이언트에서 시스템 프록시를 켜거나, 「시스템 설정」→「네트워크」→현재 네트워크 서비스→「세부사항」→「프록시」에서 설정을 확인해야 합니다. macOS 버전에 따라 메뉴가 조금씩 다를 수 있습니다. 이 컴퓨터에서만 테스트한다면 프록시 주소는 보통 127.0.0.1, 포트는 7890으로 설정합니다.

시스템 프록시가 모든 앱에 적용되는 것은 아닙니다. 일부 명령줄 도구와 게임, 자체 네트워크 스택을 사용하는 앱은 개별 설정이 필요하거나 TUN으로 트래픽을 처리해야 합니다. 먼저 HTTP 프록시를 직접 지정할 수 있는 도구로 포트가 작동하는지 확인한 다음, 트래픽 가로채기 범위를 넓힐 필요가 있는지 판단하세요.

3. dns: 수신 주소, 업스트림 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
  default-nameserver:
    - 223.5.5.5
  nameserver:
    - https://dns.alidns.com/dns-query
  fake-ip-filter:
    - "*.lan"
    - "localhost"

enable은 코어 DNS를 활성화하고, listen은 로컬 수신 주소를 지정합니다. 여기서 1053은 예시로 사용한 높은 포트 번호이며, 보통 더 높은 권한이 필요한 53 포트를 직접 사용하지 않습니다. 수신 주소만 설정해도 시스템 전체가 자동으로 이 주소에 DNS 요청을 보내는 것은 아닙니다. 시스템 DNS 설정, TUN의 DNS 가로채기, 앱 자체의 암호화 DNS에 따라 실제 요청 경로가 달라질 수 있습니다.

default-nameserver와 nameserver의 역할

  • default-nameserver는 주로 업스트림 DNS 서버의 도메인 이름을 처음 해석할 때 사용합니다. 예를 들어 먼저 dns.alidns.com을 조회한 뒤 DoH 요청을 보냅니다.
  • nameserver는 일반 도메인 조회에 사용하는 업스트림 DNS 목록입니다. 예시는 DoH를 사용하며, 실제 연결 여부는 현재 네트워크와 관련 라우팅 설정에 따라 달라집니다.
  • nameserver-policy를 사용하면 특정 도메인의 업스트림 DNS를 지정할 수 있습니다. 사내 도메인이나 회사 DNS처럼 적용 범위가 명확한 경우에 적합합니다.

노드의 server 값이 도메인 이름이라면 mihomo의 proxy-server-nameserver도 확인하세요. 노드 도메인 조회에 사용되며, 노드의 DNS 경로와 일반 웹사이트의 DNS 경로를 분리하는 데 도움이 됩니다. 일반 도메인 조회, 업스트림 서버를 찾기 위한 초기 조회, 노드 주소 조회를 같은 단계로 보면 안 됩니다.

Fake-IP는 원격 서버의 주소가 아닙니다

fake-ip 모드에서는 앱에 매핑용 주소를 반환합니다. 코어는 이 주소와 도메인의 대응 관계를 저장하고, 연결이 들어오면 이를 확인해 전달합니다. 예시에 나온 198.18.0.1/16은 매핑 주소 대역입니다. 실제 웹사이트의 위치를 판단하는 데 사용하거나 프록시 노드 주소로 취급해서는 안 됩니다.

기본 필터 동작에 따르면 fake-ip-filter에 등록된 도메인에는 Fake-IP 대신 실제 DNS 조회 결과를 반환합니다. 사내 네트워크 검색이나 실제 주소가 필요한 앱은 필요에 따라 예외로 추가할 수 있지만, DNS 업스트림에서 해당 도메인을 조회할 수 있어야 합니다. 사내 도메인을 필터 목록에 추가해도 회사 내부 DNS를 이용할 수 있게 되는 것은 아닙니다.

4. proxies와 proxy-providers: 노드 정의와 노드 제공처

proxies는 노드 목록입니다. 각 노드는 프로토콜과 연결 매개변수를 담은 항목으로 구성됩니다. 대표적인 필드는 name, type, server, port이며, 프로토콜에 따라 인증 및 전송 설정도 필요합니다. 정책 그룹은 노드 이름을 참조하므로 이름을 서로 다르게 지정하세요. 서버 포트와 로컬 mixed-port는 서로 다른 위치의 포트이므로 같은 번호일 필요가 없습니다.

필드확인할 항목
name로컬에서 사용하는 이름입니다. 정책 그룹은 이 이름으로 노드를 찾습니다.
type프로토콜 종류입니다. 예: ss, trojan, vmess. 실제 지원 여부는 코어에 따라 다릅니다.
server / port원격 서버 주소와 서비스 포트입니다. 서비스 제공자가 알려준 값을 사용합니다.
인증 및 전송 설정비밀번호, UUID, TLS, SNI, 전송 계층 설정은 서버 설정과 일치해야 합니다. 프로토콜 이름만 바꿔서는 안 됩니다.
proxy-providers별도 제공처에서 노드 목록을 불러온 다음 정책 그룹의 use로 참조합니다.

구독 전체에는 노드와 정책 그룹, 규칙이 함께 포함되는 경우가 많지만 노드 제공자는 주로 노드 목록을 제공합니다. 임의의 Base64 노드 구독 URL을 HTTP 형식의 proxy-providers에 입력해도 코어가 자동으로 형식을 변환하지 않습니다. 제공자가 지원하는 콘텐츠 형식이어야 합니다.

아직 노드가 없다면 proxies: []로 빈 목록을 표시해 설정 구조를 확인할 수 있습니다. 이 설정으로 원격 연결 출구가 만들어지는 것은 아닙니다. 이미 구독 노드가 있다면 실제 프로토콜 매개변수를 유지하고, 예시를 따라 하려고 노드 목록을 비우지 마세요.

5. proxy-groups: 노드와 규칙을 연결하는 정책 그룹

proxies: []

proxy-groups:
  - name: 수동 출구
    type: select
    proxies:
      - DIRECT

설정 구조를 확인할 때 사용할 수 있는 직접 연결 정책 그룹입니다. 수동 출구는 그룹 이름이고 select는 사용자가 구성원을 선택한다는 뜻입니다. 현재 구성원은 내장 정책 DIRECT 하나뿐이므로 직접 연결만 가능합니다. 원격 노드를 사용하려면 현재 설정에 실제로 존재하는 노드 이름을 그룹에 추가하거나 use로 노드 제공자를 참조하세요.

자주 쓰는 그룹 유형 비교

  • select: 구성원을 직접 선택합니다. 고정 출구를 사용하거나 문제를 진단할 때 적합합니다.
  • url-test: 지정한 테스트 URL로 사용 가능한 구성원의 지연 시간을 비교하고, 허용 오차 등의 설정에 따라 출구를 선택합니다.
  • fallback: 설정된 순서에 따라 사용 가능한 구성원을 선택합니다. 지연 시간을 계속 최소화하기보다 장애 전환에 초점을 둡니다.
  • load-balance: 선택한 방식에 따라 연결을 분산합니다. 단일 다운로드 연결의 대역폭을 여러 노드의 합계만큼 높여 주는 기능은 아닙니다.

테스트형 정책 그룹의 url과 interval은 노드 상태 확인에 사용됩니다. 테스트에 성공해도 테스트 대상에 연결할 수 있다는 뜻일 뿐, 모든 사이트에 접속할 수 있다는 의미는 아닙니다. 자동 선택 결과가 이상하면 먼저 테스트 URL이 현재 네트워크에 적합한지 확인한 뒤 노드를 살펴보세요.

그룹끼리 서로 참조할 수도 있지만 순환 참조를 만들면 안 됩니다. 규칙에 수동 출구를 지정했다면 설정에도 이름이 정확히 같은 그룹이 있어야 합니다. 공백이나 문장 부호가 다르면 대상을 찾지 못할 수 있습니다.

6. rule-providers: 규칙 데이터와 트래픽 분기를 분리하기

rule-providers:
  office-domains:
    type: file
    behavior: domain
    format: yaml
    path: ./rules/office-domains.yaml

이 설정은 office-domains라는 로컬 규칙 제공자를 정의합니다. type: file은 파일에서 데이터를 읽는다는 뜻이며, 예시에서는 자동 다운로드를 사용하지 않습니다. 상대 경로는 보통 텍스트 편집기의 현재 폴더가 아니라 코어의 작업 디렉터리를 기준으로 합니다. GUI 클라이언트에서 읽을 수 있는 경로를 제한할 수도 있으므로 허용된 설정 데이터 폴더에 파일을 두세요.

해당 파일의 내용은 다음과 같이 작성할 수 있습니다.

payload:
  - "+.example.org"
  - "intranet.example.net"

예약된 예시 도메인으로 구조를 설명하며 실제 사무실 서비스와는 관련이 없습니다. behavior: domain은 파일에 도메인 매칭 항목이 들어 있음을 뜻합니다. +.example.org는 루트 도메인과 하위 도메인에 모두 적용됩니다. classical을 사용하면 DOMAIN-SUFFIX,example.org처럼 규칙 유형을 포함한 항목을 작성해야 하므로 위의 순수 도메인 형식을 그대로 사용할 수 없습니다.

  • domain: 도메인 데이터에 사용하며 도메인 목록을 관리하기에 적합합니다.
  • ipcidr: IP 대역 데이터에 사용하며 주소 범위 목록을 관리하기에 적합합니다.
  • classical: 규칙 유형을 사용해 더 다양한 매칭 조건을 표현하는 목록입니다.

HTTP 형식에는 url도 필요하며, interval로 업데이트 주기를 초 단위로 지정할 수 있습니다. 예를 들어 86400은 하루입니다. format은 실제 파일 형식과 일치해야 합니다. text로 설정하면 YAML의 payload 래퍼를 사용하지 않습니다.

7. rules: 위에서부터 매칭하고 MATCH는 마지막에 배치

rules:
  - DOMAIN-SUFFIX,example.org,수동 출구
  - RULE-SET,office-domains,DIRECT
  - IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
  - GEOIP,CN,DIRECT
  - MATCH,수동 출구

이 규칙은 앞에서 정의한 정책 그룹 및 규칙 제공자와 함께 사용해야 하며, 로컬 규칙 파일도 있어야 합니다. 첫 번째 규칙은 example.org와 하위 도메인에 적용됩니다. 두 번째는 office-domains를 참조하고, 세 번째는 지정된 사설 IP 대역에 적용됩니다. 네 번째는 중국 지역 IP를 매칭하며, 마지막 규칙은 나머지 연결을 처리합니다. GEOIP 판정에는 사용 가능한 지리 데이터베이스가 필요하므로 규칙 한 줄만 추가해서는 동작하지 않습니다.

가장 먼저 일치한 규칙이 결과를 결정합니다

rules는 위에서 아래 순서로 검사하며, 규칙과 일치하면 해당 대상이 적용됩니다. 위 예시의 example.org는 사무실 규칙 목록에도 들어 있지만 첫 번째 규칙과 먼저 일치해 수동 출구를 사용합니다. 사용자 지정 예외를 일반 규칙보다 앞에 두는 이유입니다. 정확한 예외 규칙을 MATCH 뒤에 배치하면 적용되지 않습니다.

no-resolve는 IP 규칙과 일치시키기 위해 도메인 조회를 새로 시작하지 않도록 합니다. 연결에 도메인만 있고 확인할 수 있는 대상 IP가 없다면 규칙과 일치하지 않을 수 있습니다. 반면 연결 대상이 이미 IP 주소라면 IP 대역을 확인할 수 있습니다. 전체 연결에서 DNS를 차단하는 전역 옵션은 아닙니다.

정책 이름과 내장 동작

  • DIRECT: 프록시 없이 직접 연결합니다. 로컬 네트워크, 라우팅, 대상 서비스 상태의 영향을 받습니다.
  • REJECT: 일치하는 연결을 거부합니다. 차단할 대상이 명확한 경우에 적합합니다.
  • 정책 그룹 이름: 해당 그룹에서 현재 선택된 노드 또는 다음 단계의 그룹으로 연결을 전달합니다.
  • MATCH: 별도의 조건 없이 나머지 연결에 적용되는 규칙입니다. 규칙 목록의 마지막에 배치하세요.

트래픽 분기 문제를 확인할 때는 먼저 mode: rule을 유지한 채 연결 기록에서 대상 도메인, 일치한 규칙, 최종 출구를 확인하세요. 전역 모드로 전환하면 연결 결과로 이 규칙 목록의 순서를 검증할 수 없습니다.

8. TUN과 제어 인터페이스: 필요한 범위에 맞게 활성화

tun:
  enable: false
  stack: mixed
  auto-route: true
  auto-detect-interface: true
  dns-hijack:
    - any:53

예시는 프록시 포트와 규칙을 단계별로 확인할 수 있도록 TUN을 꺼 둡니다. 시스템 프록시를 따르지 않는 앱까지 처리해야 한다면 클라이언트의 서비스 설치, 시스템 권한, 코어 지원 여부를 확인한 다음 활성화하세요. auto-route는 라우팅을 자동으로 설정하고, auto-detect-interface는 출구 네트워크 인터페이스 선택을 돕습니다. TUN을 켠 뒤에는 스위치 상태뿐 아니라 네트워크 라우팅도 확인해야 합니다.

dns-hijack의 any:53은 해당하는 일반 DNS 트래픽을 가로채는 설정이며, 모든 DoH 또는 DoT 요청까지 직접 처리한다는 뜻은 아닙니다. 회사 VPN, 다른 가상 네트워크 인터페이스, 방화벽이 TUN 경로와 충돌할 수 있습니다. 문제가 생기면 DNS와 라우팅, 모든 규칙을 한꺼번에 바꾸기보다 먼저 한 가지 트래픽 처리 기능을 꺼서 차이를 확인하세요.

external-controller는 제어 API가 요청을 받는 포트로, 웹 프록시 포트와는 다릅니다. 예를 들어 로컬 관리 주소는 127.0.0.1:9090으로 설정할 수 있습니다. 사용해야 한다면 secret에 강력한 비밀번호를 설정하고 접근 범위를 제한하세요. 일부 클라이언트는 이 필드를 자체 관리하므로 클라이언트가 사용하는 주소와 인증 설정을 직접 덮어쓰지 않는 것이 좋습니다.

9. 수정 후 확인: 문법, 포트, 출구 순으로 점검

  1. 먼저 문법을 확인하세요. mihomo 독립 실행 명령을 설치했다면 설정 파일 복사본이 있는 디렉터리에서 아래 명령으로 검사할 수 있습니다. GUI 클라이언트는 설정 검사 또는 다시 불러오기 기능으로 오류를 확인해도 됩니다.
  2. 다음으로 포트 상태를 확인하세요. 7890을 다른 클라이언트가 사용 중이지 않은지, 현재 실행 중인 프로세스가 수정한 파일을 읽고 있는지 확인합니다.
  3. 프록시를 직접 지정해 테스트하세요. 프록시 주소를 명시해 요청하면 시스템 프록시 스위치와 앱 자체 설정의 영향을 제외할 수 있습니다.
  4. 마지막으로 규칙을 확인하세요. 연결 기록에서 적용된 규칙과 출구를 살펴보고, 필요한 경우 기존 연결을 끊은 뒤 다시 시도하세요. 이미 연결된 장시간 연결은 이전 경로를 계속 사용할 수 있습니다.
mihomo -t -f ./config.yaml

# macOS: TCP 7890을 수신 중인 프로세스 확인
lsof -nP -iTCP:7890 -sTCP:LISTEN

# 로컬 HTTP 프록시를 직접 지정해 요청 테스트
curl -I --proxy http://127.0.0.1:7890 https://example.com

설정 검사가 성공하면 현재 코어가 파일을 읽을 수 있다는 뜻입니다. 노드가 온라인인지, 룰셋 다운로드가 성공했는지, 대상 사이트에 연결할 수 있는지까지 확인된 것은 아닙니다. curl이 HTTP 응답을 받았다는 것은 이번 요청에 응답이 왔다는 뜻입니다. 실제 연결이 직접 연결인지 프록시를 거쳤는지는 연결 기록에서 확인하세요. 이 글의 노드가 비어 있는 예시에는 직접 연결 항목만 있으므로 원격 프록시 연결을 테스트할 수 없습니다.

오류 메시지에 따라 해당 필드를 확인하세요

  • YAML 파싱 실패: 오류가 표시된 줄 주변의 들여쓰기, 따옴표, 콜론, 목록 하이픈을 확인하세요.
  • 프록시 또는 정책 그룹을 찾을 수 없음: 규칙 대상, 그룹 구성원, 노드 이름을 확인하고 노드 제공자가 로드됐는지 점검하세요.
  • 룰셋을 읽지 못함: 경로와 파일 권한, behavior, format이 실제 파일 내용과 일치하는지 확인하세요.
  • 포트 바인딩 실패: 포트 사용 여부와 수신 주소를 확인하세요. 두 클라이언트가 같은 포트를 동시에 사용하지 않도록 합니다.
  • 구독 업데이트 후 수정 사항이 사라짐: 계속 유지할 설정을 클라이언트가 지원하는 로컬 오버라이드로 옮긴 다음, 병합된 런타임 설정을 확인하세요.

정상적으로 불러올 수 있는 기준 설정을 하나 보관하고, 한 번에 한 종류의 설정만 변경하세요. 포트, DNS, 노드와 정책 그룹, 규칙과 TUN 순으로 확인하면 문제가 방금 수정한 부분으로 좁혀집니다. 전체 설정 방법은 사이트의 고급 설정 안내에서 확인할 수 있습니다.