Clash API로 프록시 노드를 자동 전환해야 하는 이유
프록시를 오래 사용하다 보면 처음에는 잘 열리던 웹사이트가 어느 순간 느려지거나, 특정 노드의 연결이 끊긴 뒤 모든 요청이 멈추는 일이 생깁니다. 수동으로 Clash 노드 변경을 할 수는 있지만, 다운로드·터미널 작업·API 호출처럼 사용자가 화면을 계속 보고 있지 않은 상황에서는 대응이 늦어집니다. 이때 Mihomo의 외부 컨트롤러 API를 이용하면 현재 선택된 노드의 지연 시간을 확인하고, 기준을 넘었을 때 다른 노드로 바꾸는 자동화 흐름을 만들 수 있습니다.
이 글에서 말하는 자동 전환은 단순히 무작위로 노드를 바꾸는 방식이 아닙니다. 먼저 프록시 그룹의 이름과 멤버를 API로 읽고, 후보 노드에 실제 지연 시간 테스트를 요청한 다음, 정해 둔 임계값과 실패 횟수에 따라 다음 노드를 선택합니다. 따라서 Clash API 자동화를 처음 구성하는 사용자도 어떤 단계에서 문제가 생겼는지 추적할 수 있습니다. GUI에서 TUN 모드를 켜는 것과는 별개로, 이 기능의 중심은 Mihomo 코어의 API와 설정 파일입니다.
사용 전제: 이 글은 본인이 관리하는 Mihomo 또는 Clash 인스턴스에서 합법적으로 사용할 수 있는 프록시를 안정화하는 방법을 설명합니다. 외부 컨트롤러 포트를 인터넷에 공개하거나 타인의 API에 접근하지 마세요.
자동 전환의 작동 구조
전체 흐름은 네 부분으로 나뉩니다. 첫째, Mihomo가 외부 컨트롤러를 통해 HTTP API를 엽니다. 둘째, 스크립트가 인증 헤더를 포함해 현재 프록시 그룹과 노드 목록을 조회합니다. 셋째, 각 노드에 테스트 URL을 요청해 지연 시간 또는 실패 여부를 기록합니다. 넷째, 선택 API에 원하는 노드를 전달합니다. GUI의 Proxies 화면에서 버튼을 누르는 작업을 스크립트가 대신한다고 이해하면 쉽습니다.
- 컨트롤러 주소: 보통
127.0.0.1:9090처럼 로컬 주소를 사용합니다. - 인증 토큰:
secret값과 API 요청의Authorization: Bearer헤더가 일치해야 합니다. - 프록시 그룹: 실제로 노드를 선택하는 그룹입니다. 설정에 따라 이름이
Proxy,GLOBAL, 또는 사용자 정의 그룹일 수 있습니다. - 테스트 URL: 항상 응답이 빠르고 안정적인 HTTPS 주소를 사용하며, 서비스 약관과 네트워크 정책을 확인합니다.
API 활성화와 보안 설정
먼저 현재 사용 중인 프로필에서 외부 컨트롤러 설정을 확인합니다. Mihomo YAML에서는 다음과 같은 항목을 사용합니다. 이미 GUI에서 컨트롤러 포트를 설정했다면 중복으로 다른 포트를 열지 말고, 실제 로그나 설정 화면에 표시된 값을 기준으로 맞추세요.
external-controller: 127.0.0.1:9090
secret: "change-this-to-a-long-random-token"
external-controller를 0.0.0.0:9090으로 바꾸면 다른 장치에서도 접속할 수 있지만, 방화벽 규칙과 인증을 잘못 설정했을 때 제어 권한이 외부로 노출될 수 있습니다. 특별한 이유가 없다면 127.0.0.1에 바인딩하세요. 자동 전환 스크립트와 Mihomo가 같은 컴퓨터에서 실행되는 경우 로컬 바인딩만으로 충분합니다.
토큰 보호: secret 값은 구독 URL이나 공개 저장소에 넣지 마세요. 셸 기록, 배치 파일, Git 커밋에 남지 않도록 환경 변수나 별도 권한의 설정 파일을 사용하고, 이미 노출했다면 즉시 새 토큰으로 교체합니다.
API가 열렸는지는 다음과 같이 확인할 수 있습니다. 토큰을 생략했을 때 401이 반환되고, 올바른 토큰을 사용했을 때 JSON 응답이 나오면 인증 단계는 정상입니다.
curl -H "Authorization: Bearer change-this-to-a-long-random-token" \
http://127.0.0.1:9090/version
Windows에서는 PowerShell의 Invoke-RestMethod, macOS와 Linux에서는 curl을 이용하면 됩니다. 연결이 거부되면 포트 번호, Mihomo 실행 여부, GUI가 실제로 사용하는 컨트롤러 주소를 차례로 확인합니다. 응답은 오지만 401 Unauthorized가 나오면 토큰 앞뒤의 공백과 따옴표를 점검하세요.
프록시 그룹과 노드 상태 확인하기
자동화 전에 API를 수동으로 한 번 호출해 두면 그룹 이름을 잘못 입력하는 실수를 줄일 수 있습니다. 전체 프록시 정보를 조회하는 엔드포인트는 /proxies입니다. 응답에는 프록시 그룹, 현재 선택된 노드, 그룹에 포함된 모든 멤버가 함께 들어 있습니다.
curl -H "Authorization: Bearer $CLASH_SECRET" \
http://127.0.0.1:9090/proxies
반환된 JSON에서 자동 전환 대상 그룹을 찾습니다. 예를 들어 그룹 이름이 Proxy이고 현재 노드가 Tokyo-01이라면, 스크립트가 사용할 실제 경로는 /proxies/Proxy입니다. 그룹 이름에 공백이나 특수문자가 있다면 URL 인코딩을 적용해야 하므로, 가능하면 영문과 숫자로 된 짧은 이름을 쓰는 편이 관리하기 쉽습니다.
특정 노드의 지연 시간을 테스트할 때는 /proxies/{name}/delay 엔드포인트를 사용합니다. 테스트 URL과 제한 시간은 URL 쿼리로 전달합니다.
curl -G \
-H "Authorization: Bearer $CLASH_SECRET" \
--data-urlencode "url=https://www.gstatic.com/generate_204" \
--data-urlencode "timeout=5000" \
http://127.0.0.1:9090/proxies/Tokyo-01/delay
성공하면 밀리초 단위의 지연 시간이 반환됩니다. 응답이 없거나 제한 시간을 초과하면 해당 노드를 즉시 불량으로 단정하지 말고, 두세 차례 연속 실패했는지 확인하는 것이 좋습니다. 일시적인 DNS 지연이나 원격 테스트 URL의 순간적인 장애 때문에 정상 노드가 교체될 수 있기 때문입니다.
지연 시간 기준으로 노드를 바꾸는 스크립트
다음은 Python 표준 라이브러리만 사용해 현재 그룹의 후보 노드를 점검하고 가장 빠른 노드를 선택하는 기본 예시입니다. 실제 환경에서는 그룹 이름과 포트, 테스트 URL을 자신의 설정에 맞게 바꾸세요. 이 방식은 외부 패키지 설치가 필요하지 않아 서버나 개발용 PC에서 실행하기 편합니다.
import json
import os
import time
import urllib.parse
import urllib.request
API = os.getenv("CLASH_API", "http://127.0.0.1:9090")
SECRET = os.environ["CLASH_SECRET"]
GROUP = os.getenv("CLASH_GROUP", "Proxy")
TEST_URL = os.getenv("CLASH_TEST_URL", "https://www.gstatic.com/generate_204")
TIMEOUT = 5000
LIMIT = 800
def request(path, method="GET", data=None):
headers = {"Authorization": f"Bearer {SECRET}"}
body = None
if data is not None:
headers["Content-Type"] = "application/json"
body = json.dumps(data).encode()
req = urllib.request.Request(API + path, headers=headers,
method=method, data=body)
with urllib.request.urlopen(req, timeout=8) as response:
return json.loads(response.read().decode())
proxies = request("/proxies")
group = proxies[GROUP]
candidates = [name for name in group["all"]
if name not in ("DIRECT", "REJECT")]
results = []
for name in candidates:
encoded = urllib.parse.quote(name, safe="")
query = urllib.parse.urlencode({
"url": TEST_URL,
"timeout": str(TIMEOUT)
})
try:
result = request(f"/proxies/{encoded}/delay?{query}")
delay = int(result["delay"])
results.append((delay, name))
print(f"{name}: {delay} ms")
except Exception as error:
print(f"{name}: failed ({error})")
if results:
results.sort()
delay, selected = results[0]
current = group.get("now")
if selected != current and delay <= LIMIT:
request(f"/proxies/{urllib.parse.quote(GROUP, safe='')}",
method="PUT", data={"name": selected})
print(f"switched to {selected}: {delay} ms")
else:
print(f"keep {current}: best={delay} ms")
else:
print("no healthy proxy found")
이 예시는 가장 빠른 노드를 고르지만, 실제 운영에서는 단순 최저 지연 시간만으로 결정하지 않는 편이 안정적입니다. 지연 시간이 20ms 낮더라도 패킷 손실이 심하거나 특정 서비스와 호환되지 않을 수 있습니다. 따라서 다음과 같은 조건을 함께 고려하세요.
- 지연 시간이 800ms를 초과할 때만 전환해 짧은 변동에 반응하지 않게 합니다.
- 현재 노드가 정상이고 새 노드와 차이가 작으면 기존 연결을 유지합니다.
- 한 번의 실패가 아니라 연속된 두세 번의 실패를 전환 조건으로 사용합니다.
- 전환 후에는 30초에서 60초 정도 대기해 노드가 계속 바뀌는 현상을 막습니다.
- 자동 선택에서
DIRECT,REJECT, 특별한 스트리밍 전용 노드는 제외합니다.
주기 실행과 중복 실행 방지
Linux와 macOS에서는 cron 또는 systemd timer로 5분마다 실행할 수 있고, Windows에서는 작업 스케줄러에 Python 명령을 등록할 수 있습니다. 다만 너무 짧은 주기는 API 호출을 불필요하게 늘리고, 노드 제공업체의 테스트 제한에 걸릴 수 있습니다. 일반적인 웹 사용이라면 3~10분 간격부터 시작하고, 영상·장시간 다운로드처럼 연결 안정성이 중요한 작업만 별도 프로필로 관리하세요.
스크립트가 겹쳐 실행되지 않도록 잠금도 추가하는 것이 좋습니다. 이전 실행이 아직 노드 테스트 중인데 다음 실행이 시작되면 서로 다른 결과를 번갈아 적용할 수 있습니다. Linux에서는 flock, Windows에서는 작업 스케줄러의 “새 인스턴스를 시작하지 않음” 옵션을 활용할 수 있습니다. API 토큰은 다음처럼 환경 변수로 전달하면 소스 코드에 직접 기록하지 않아도 됩니다.
export CLASH_SECRET='replace-with-your-secret'
export CLASH_GROUP='Proxy'
python3 switch_node.py
운영 팁: 자동 전환은 항상 켜 두기보다 업무 시간이나 특정 명령을 실행할 때만 활성화하는 편이 예측 가능할 때도 있습니다. 특히 SSH 세션, 대용량 업로드, 로그인 과정에서는 중간에 노드가 바뀌면 연결이 끊길 수 있습니다.
전환 실패와 로그를 확인하는 순서
스크립트가 실행됐는데 노드가 바뀌지 않는다면 먼저 “자동화 로직”과 “Mihomo 동작”을 분리해 확인합니다. API의 /version과 /proxies가 모두 응답하는지 확인한 뒤, 지연 시간 API를 단일 노드에 직접 호출합니다. 여기까지 성공했는데 선택이 실패하면 그룹명이 URL 인코딩되지 않았거나, PUT 요청의 JSON 필드가 name이 아닌 경우를 의심할 수 있습니다.
- 401 오류: 토큰이 없거나
Bearer표기, 공백, 환경 변수 이름이 잘못된 경우입니다. - 404 오류: 그룹 또는 노드 이름이 실제 JSON과 다릅니다. 대소문자와 공백을 그대로 복사하세요.
- 400 오류: 선택할 수 없는 그룹에 PUT 요청을 보냈거나 JSON 형식이 올바르지 않을 수 있습니다.
- 응답 지연: 테스트 URL이 불안정하거나 DNS·TUN·현재 노드 자체에 문제가 있을 수 있습니다.
- 전환은 되지만 앱이 끊김: 노드 변경에 따라 기존 TCP 연결이 재설정되는 정상적인 현상일 수 있습니다.
GUI의 Logs 또는 Connections 화면에서는 요청 시각, 목적지 도메인, 매칭된 규칙, 사용된 프록시 그룹을 함께 확인합니다. 자동 전환 직후 모든 연결이 DIRECT로 표시된다면 그룹 선택 문제가 아니라 YAML의 규칙 우선순위 문제일 수 있습니다. 반대로 API 테스트는 성공하지만 특정 앱만 실패한다면 해당 앱이 시스템 프록시를 따르지 않는지, TUN 모드가 활성화됐는지, UDP 연결이 필요한지 점검해야 합니다.
로그는 결과만 남기기보다 판단 근거를 남겨야 합니다. 최소한 실행 시각, 현재 노드, 테스트 결과, 선택된 노드, 전환 사유를 기록하세요. 예를 들어 “현재 1200ms, 후보 Singapore-02 410ms, 임계값 800ms 초과로 전환”처럼 남기면 나중에 노드 품질 저하와 스크립트 오작동을 구분하기 쉽습니다. 토큰과 구독 URL은 로그에 절대 출력하지 않습니다.
시중의 일부 자동 선택 도구는 노드 목록을 읽는 기능은 있어도, Mihomo API 인증 방식이나 GUI별 그룹 구조를 충분히 설명하지 않아 오류가 나면 처음부터 설정을 다시 해야 합니다. 반대로 Clash 공식 사이트는 Clash와 Mihomo의 설정 차이, API 보안, 지연 시간 측정, 로그 확인을 한 흐름으로 정리해 직접 검증하면서 적용하기 쉽습니다. 노드가 자주 끊기거나 수동 전환이 번거로운 환경이라면 먼저 Clash 클라이언트 다운로드에서 사용 중인 플랫폼에 맞는 도구를 확인한 뒤, 이 글의 API 설정을 작은 그룹에서 시험해 보세요.