왜 Clash API로 노드를 자동 전환해야 하는가
프록시 노드는 항상 같은 품질을 유지하지 않습니다. 같은 서버라도 저녁 시간대에는 사용자가 몰려 지연 시간이 크게 늘 수 있고, 일시적인 패킷 손실이나 upstream 장애 때문에 연결은 살아 있지만 실제 웹 요청은 자주 끊길 수 있습니다. 반대로 화면에 표시되는 지연 시간이 조금 높더라도 장시간 스트리밍이나 API 요청에는 더 안정적인 노드가 있을 수 있습니다. 이런 상황에서 사용자가 Clash 화면을 열고 노드를 하나씩 눌러 바꾸는 방식은 문제를 늦게 발견하게 만들고, 반복 작업도 상당히 늘립니다.
Clash API를 사용하면 현재 실행 중인 코어의 프록시 목록, 정책 그룹, 연결 상태를 프로그램에서 조회할 수 있습니다. Python 같은 스크립트로 여러 노드의 상태를 확인한 뒤 기준에 맞는 노드를 선택하고, 정책 그룹의 현재 멤버를 API로 변경할 수 있습니다. 이 방식은 단순히 가장 낮은 숫자의 노드를 고르는 것이 아니라, 지연 시간·HTTP 응답 성공 여부·연속 실패 횟수·최근 전환 시간까지 함께 고려하는 자동화에 적합합니다.
다만 API 자동화는 “빠른 노드 하나를 계속 선택하는 기능”으로 이해하면 안 됩니다. 측정 대상 URL, 테스트 주기, 실패 허용 횟수, 노드 전환 후 안정화 시간에 따라 결과가 크게 달라집니다. 너무 짧은 주기로 검사하면 모든 노드가 정상이어도 불필요하게 전환되고, 한 번의 일시적인 timeout만으로 노드를 제외하면 오히려 연결이 불안정해집니다. 따라서 먼저 목표를 정해야 합니다. 웹 브라우징용인지, 장시간 API 스트리밍용인지, 다운로드용인지에 따라 건강한 노드의 기준이 달라집니다.
127.0.0.1에만 열려 있다면 같은 컴퓨터의 스크립트만 접근할 수 있으며, LAN 주소나 공용 주소에 노출할 때는 반드시 인증과 방화벽을 함께 설정해야 합니다.
외부 컨트롤러와 API 권한 준비
Clash 계열 클라이언트에서 API 이름은 버전에 따라 External Controller, External Controller API, 외부 컨트롤러 등으로 표시됩니다. Clash Verge, Clash Verge Rev, Mihomo Party에서는 설정 화면이나 코어 설정 파일에서 주소와 포트를 확인할 수 있고, 일부 클라이언트는 기본값으로 로컬 주소의 특정 포트를 사용합니다. 중요한 것은 화면에 보이는 포트 번호를 추측하지 말고 실제 실행 중인 코어의 설정과 로그를 기준으로 확인하는 것입니다.
일반적인 접근 주소는 http://127.0.0.1:9090과 같은 형태입니다. API에 비밀키가 설정되어 있다면 모든 요청에 Authorization: Bearer 비밀키 헤더를 넣어야 합니다. 비밀키를 설정하지 않은 상태에서 컨트롤러를 0.0.0.0에 바인딩하면 같은 네트워크의 다른 장치가 프록시 그룹을 바꾸거나 연결 정보를 조회할 수 있으므로 피해야 합니다. 특히 서버나 공유기에서 실행할 때는 외부 컨트롤러를 인터넷에 직접 노출하지 말고 localhost, 방화벽, SSH 터널 같은 제한된 접근 구조를 사용하세요.
자동 전환 스크립트가 읽어야 하는 정보는 대체로 세 종류입니다. 첫째는 /proxies 응답으로, 현재 설정에 등록된 프록시와 정책 그룹을 확인합니다. 둘째는 특정 노드의 지연 시간을 측정하는 /proxies/{name}/delay 요청입니다. 셋째는 정책 그룹에 어떤 노드가 선택되어 있는지 확인하고 변경하는 작업입니다. 그룹 이름에 공백, 이모지, 한글이 들어갈 수 있으므로 URL 경로에 이름을 직접 이어 붙이지 말고 Python의 URL 인코딩 기능을 활용해야 합니다.
import requests
CONTROLLER = "http://127.0.0.1:9090"
HEADERS = {"Authorization": "Bearer CHANGE_THIS_SECRET"}
response = requests.get(
f"{CONTROLLER}/proxies",
headers=HEADERS,
timeout=5
)
response.raise_for_status()
proxy_data = response.json()
print(proxy_data.keys())
위 코드는 연결 가능 여부와 JSON 응답만 확인하는 최소 예시입니다. 운영용으로 확장할 때는 비밀키를 소스 코드에 직접 적지 말고 환경 변수나 별도의 권한 제한 파일에서 읽는 편이 안전합니다. 또한 API 응답이 HTML 오류 페이지로 돌아오거나 코어가 재시작 중일 때 JSON 파싱에서 예외가 발생할 수 있으므로, 상태 코드와 예외를 모두 기록해야 합니다. 첫 단계에서는 “노드 전환”보다 “현재 그룹과 노드 목록을 정확히 읽는가”를 먼저 검증하세요.
지연 시간과 연결 상태를 함께 측정하는 방법
가장 간단한 자동 선택은 각 노드에 대해 지연 시간 API를 호출하고 가장 작은 값을 고르는 방식입니다. 그러나 지연 시간 하나만으로 품질을 판단하면 잘못된 선택이 생깁니다. ICMP 기반 핑은 빠르지만 실제 HTTPS 요청이 실패할 수 있고, 짧은 HTTP 응답은 성공해도 장시간 스트리밍 연결이 중간에 끊길 수 있습니다. 따라서 측정 URL은 실제 사용 목적과 가까워야 합니다. 브라우징이라면 작고 안정적인 HTTPS 응답을, API 작업이라면 실제로 자주 연결하는 서비스의 상태 확인 URL을 별도로 고려합니다.
Clash 코어가 제공하는 delay API는 보통 노드 이름, 테스트 URL, timeout 값을 쿼리로 전달합니다. 노드 이름에 특수문자가 있으면 반드시 인코딩해야 하며, timeout은 너무 길게 설정하지 않는 것이 좋습니다. 예를 들어 5초 timeout을 사용하되, 한 번 실패했다고 즉시 제외하지 않고 짧은 간격으로 두세 번 재검사하는 식이 더 안정적입니다. 테스트 URL이 특정 지역에서 차단되거나 CDN 정책에 따라 응답 시간이 변하면 모든 노드가 나쁜 것처럼 보일 수 있으므로, 한 개의 URL만 절대적인 기준으로 사용하지 않는 것도 중요합니다.
실전에서는 다음과 같은 점수 구조를 사용할 수 있습니다. 지연 시간이 200ms 이하이면 높은 점수를 주고, 500ms를 넘으면 감점합니다. HTTP 응답 실패나 timeout은 큰 감점을 적용하되, 최근 검사에서 성공한 노드에는 작은 보정값을 둡니다. 같은 노드가 연속으로 실패한 횟수를 저장하고, 두 번 또는 세 번 연속 실패했을 때만 교체 대상으로 확정합니다. 반대로 새 노드의 점수가 현재 노드보다 일정 수준 이상 좋을 때만 전환하면 두 노드의 점수가 비슷한 상황에서 선택이 계속 흔들리는 현상을 줄일 수 있습니다.
- 측정 간격: 일반 브라우징은 60~180초, 장시간 작업은 30~60초부터 시작합니다.
- timeout: 회선 환경에 맞추되, 짧은 API 호출은 3~5초 범위에서 검토합니다.
- 실패 기준: 한 번의 실패가 아니라 연속 실패 횟수를 사용합니다.
- 전환 차이: 새 노드가 현재 노드보다 50~100ms 이상 좋을 때만 교체하는 식으로 여유값을 둡니다.
- 안정화 시간: 전환 직후 즉시 재평가하지 않고 일정 시간 기다립니다.
운영 원칙: “가장 빠른 노드”보다 “정해진 시간 동안 요청이 성공하는 노드”가 더 좋은 기본값인 경우가 많습니다. 특히 로그인 세션, WebSocket, 장시간 API 스트리밍은 측정 순간의 최저 지연보다 경로가 자주 바뀌지 않는 안정성이 중요합니다.
아래와 같이 노드별 결과를 별도의 기록으로 남기면 원인 분석도 쉬워집니다. 노드 이름, 측정 시각, 지연 시간, HTTP 상태, 예외 유형, 선택 여부를 CSV나 JSON으로 저장하면 제공자의 특정 지역 노드가 매일 같은 시간에 느려지는지 확인할 수 있습니다. 로그에는 API 비밀키나 구독 URL을 기록하지 말고, 노드 이름에도 계정 정보가 포함되어 있는지 확인하세요.
from urllib.parse import quote
import requests
def check_delay(controller, secret, node, test_url):
encoded = quote(node, safe="")
headers = {"Authorization": f"Bearer {secret}"}
endpoint = f"{controller}/proxies/{encoded}/delay"
params = {"url": test_url, "timeout": 5000}
try:
res = requests.get(endpoint, headers=headers, params=params, timeout=8)
res.raise_for_status()
return {"node": node, "delay": res.json().get("delay"), "ok": True}
except requests.RequestException as exc:
return {"node": node, "delay": None, "ok": False, "error": str(exc)}
정책 그룹을 안전하게 전환하는 Python 스크립트
노드를 선택했다면 정책 그룹의 현재 멤버를 확인한 뒤 변경해야 합니다. 사용자가 실제 트래픽에 연결해 둔 그룹을 직접 바꾸기보다는, 자동 전환 전용 그룹을 하나 만들고 규칙에서 그 그룹을 참조하게 하는 편이 안전합니다. 예를 들어 그룹 이름을 AUTO-WORK로 고정하고 그 안에 자동화 대상 노드만 넣으면, 스트리밍용 노드와 일반 웹용 노드가 서로 영향을 덜 줍니다. 그룹 이름은 구독 갱신으로 사라지지 않도록 로컬 오버라이드나 클라이언트가 지원하는 패치 구조로 관리해야 합니다.
Mihomo 계열 API에서는 선택형 정책 그룹에 새 프록시 이름을 PUT 방식으로 전달하는 구성이 널리 사용됩니다. 다만 코어와 클라이언트 버전에 따라 지원하는 그룹 타입과 API 동작이 다를 수 있으므로, 먼저 현재 그룹의 JSON을 조회하고 테스트용 그룹에서 확인하세요. url-test나 fallback처럼 코어가 내부적으로 선택하는 그룹은 사용자가 직접 멤버를 PUT하는 방식과 목적이 다를 수 있습니다. 자동화 스크립트가 직접 노드를 고르려면 선택형 그룹을 별도로 두는 것이 이해와 복구에 유리합니다.
def switch_group(controller, secret, group, node):
encoded_group = quote(group, safe="")
url = f"{controller}/proxies/{encoded_group}"
headers = {
"Authorization": f"Bearer {secret}",
"Content-Type": "application/json"
}
res = requests.put(
url,
headers=headers,
json={"name": node},
timeout=5
)
res.raise_for_status()
return True
실제 전환 루프에서는 다음 순서를 지키는 것이 좋습니다. 먼저 API가 응답하는지 확인하고, 그룹에 대상 노드가 포함되어 있는지 검사합니다. 그다음 후보 노드의 상태를 측정하고, 현재 선택된 노드와 새 후보의 점수 차이를 비교합니다. 전환이 필요할 때만 PUT 요청을 보내며, 성공 응답을 받은 뒤 다시 /proxies를 조회해 실제 선택값이 바뀌었는지 확인합니다. 마지막으로 10~30초 정도 기다린 뒤 애플리케이션의 실제 연결 로그를 살펴야 합니다. API가 200을 반환했다는 사실만으로 모든 기존 연결이 새 노드로 이동했다고 판단해서는 안 됩니다.
중복 실행 방지도 반드시 넣어야 합니다. 운영체제 작업 스케줄러나 cron이 같은 스크립트를 여러 번 실행하면 서로 다른 프로세스가 동시에 그룹을 바꿀 수 있습니다. 간단한 lock 파일이나 프로세스 락을 사용하고, 마지막 전환 시각을 저장해 최소 전환 간격을 강제하세요. 재시작 후에도 실패 횟수와 마지막 선택 노드를 유지하고 싶다면 작은 JSON 상태 파일을 사용할 수 있습니다. 상태 파일이 손상되었을 때 전체 자동화를 멈추지 않도록 백업 파일과 기본값도 준비해 두는 편이 좋습니다.
0.0.0.0 바인딩과 공용 방화벽 허용을 피하세요. 자동화가 필요하더라도 우선 localhost에서 실행하고, 원격 제어가 꼭 필요할 때만 별도의 인증 계층과 네트워크 제한을 추가하세요.
전환이 실패할 때의 점검 순서와 운영 기준
스크립트가 실행되지만 노드가 바뀌지 않는다면 먼저 API 주소, 포트, 비밀키를 확인합니다. 401이나 403은 인증 헤더 문제일 가능성이 높고, 404는 그룹 이름 인코딩이나 엔드포인트 차이를 의심해야 합니다. 400이 반환되면 그룹 타입이 선택형이 아닌지, PUT 본문의 필드명이 코어가 요구하는 형식인지, 전달한 노드가 해당 그룹에 실제로 등록되어 있는지 확인합니다. 응답이 아예 없다면 Clash 코어가 종료되었거나 컨트롤러 포트가 다른 프로세스에 사용되고 있을 수 있습니다.
노드 측정 결과가 모두 실패하는 경우에는 자동 전환 로직보다 테스트 경로를 먼저 분리해야 합니다. 같은 테스트 URL을 브라우저에서 열어 보되, 브라우저의 시스템 프록시와 스크립트의 API 측정 경로가 같은지 확인합니다. Clash 로그에서 테스트 요청이 실제로 어느 그룹을 통과하는지, DNS 해석이 실패했는지, TLS 단계에서 끊겼는지 확인하세요. DNS 문제를 지연 시간 문제로 오해하면 계속 다른 노드만 선택하게 됩니다. 필요하면 노드 목록을 세 그룹으로 나눠 한 그룹씩 검사해 공급자 전체 장애와 특정 노드 장애를 구분합니다.
자동화는 모니터링을 대체하지 않습니다. 최소한 현재 선택 노드, 최근 측정값, 전환 이유, 실패 횟수, API 오류를 로그에 남기고, 일정 시간 동안 모든 후보가 실패하면 무한히 전환하지 말고 마지막 정상 노드를 유지해야 합니다. 전환 횟수가 짧은 시간에 급증하면 알림을 보내거나 자동화를 일시 중지하는 회로 차단도 유용합니다. 사용자가 수동으로 고정할 수 있는 환경 변수나 “자동 선택 중지” 파일을 마련해 두면 장애 분석 중 스크립트가 상태를 덮어쓰는 문제를 막을 수 있습니다.
단순한 GUI의 수동 선택은 처음 설정할 때 직관적이지만, 여러 노드의 상태를 주기적으로 비교하거나 전환 이력을 남기는 기능은 제한적일 수 있습니다. 반대로 일부 제공자 전용 자동 도구는 특정 코어 버전에 종속되고, 노드 이름이나 그룹 구조가 바뀌면 복구가 어렵습니다. Clash API와 Python을 조합하면 이러한 단점을 보완해 측정 기준, 실패 횟수, 전환 간격을 직접 통제하고 Mihomo 코어와 함께 운영할 수 있습니다. API 권한과 스크립트 관리가 부담스럽더라도, 로컬에서 안전하게 시작해 자동 전환 전용 그룹 하나만 검증해 보려면 Clash V.CORE를 다운로드해 현재 사용 환경에 맞는 API 설정과 노드 운영을 먼저 준비하는 편이 좋습니다.