Clash의 external-controller는 단순히 대시보드를 연결하는 옵션이 아닙니다. HTTP API를 활성화하면 현재 프록시 그룹, 노드별 연결 상태와 응답 지연을 조회하고, 조건에 따라 선택된 노드를 자동으로 변경할 수 있습니다. 기본 제공되는 url-testfallback 그룹만으로 충분한 경우도 있지만, 특정 도메인에 대한 접속 실패, 연속적인 지연 증가, 특정 노드의 인증 오류처럼 더 구체적인 조건을 처리하려면 API 기반 자동화가 유용합니다. 이 글에서는 Mihomo와 Clash 계열 클라이언트에서 외부 컨트롤러를 안전하게 열고, 상태 조회 API를 호출하며, 장애가 발생했을 때 다른 노드로 전환하는 실전 구성을 단계별로 설명합니다.

external-controller 안전하게 활성화하기

외부 컨트롤러는 Clash의 HTTP 관리 서버입니다. 127.0.0.1:9090으로 설정하면 같은 컴퓨터에서만 접근할 수 있고, 0.0.0.0:9090으로 설정하면 LAN의 다른 기기에서도 접근할 수 있습니다. 자동 전환 스크립트를 동일한 컴퓨터에서 실행한다면 로컬 바인딩이 가장 안전합니다. 다른 서버나 홈 서버에서 관리해야 할 때만 LAN 바인딩을 고려하세요.

권장 external-controller 설정
# config.yaml
external-controller: 127.0.0.1:9090
secret: change-this-to-a-long-random-secret

# LAN의 관리 서버에서 접근해야 하는 경우에만 사용
# external-controller: 0.0.0.0:9090

API 인증은 요청 헤더의 Authorization에 Bearer 토큰을 넣는 방식입니다. secret을 설정했다면 모든 상태 조회와 변경 요청에 다음 헤더가 필요합니다. 시크릿을 비워 두면 인증 없이 API가 열리므로 테스트 환경 외에는 권장하지 않습니다.

API 인증 헤더 예시
Authorization: Bearer change-this-to-a-long-random-secret
Content-Type: application/json

Clash Verge, Clash Verge Rev, Clash for Windows, Clash for Android와 같은 클라이언트는 코어 종류와 버전에 따라 설정 화면의 위치가 다를 수 있습니다. 그러나 실제로 API를 사용하는 핵심은 실행 중인 코어의 external-controller 주소를 확인하는 것입니다. 설정을 수정한 뒤에는 코어를 재시작하고 /version 엔드포인트로 응답을 확인하세요.

컨트롤러 연결 확인
curl -s \
  -H "Authorization: Bearer change-this-to-a-long-random-secret" \
  http://127.0.0.1:9090/version
관리 포트를 인터넷에 직접 공개하지 마세요: 외부 컨트롤러는 노드 선택뿐 아니라 설정 변경, 연결 종료, 프로필 관리까지 수행할 수 있습니다. 공용 IP에 포트를 열어야 한다면 방화벽 허용 목록, VPN, 역방향 프록시의 추가 인증을 함께 사용하고, 짧거나 예측하기 쉬운 시크릿은 피하세요.

노드 상태와 지연 조회하기

자동 전환의 첫 단계는 현재 프록시 그룹과 그 안에 포함된 노드를 식별하는 것입니다. GET /proxies는 모든 프록시와 그룹의 상세 정보를 반환합니다. 응답에는 프록시 그룹의 현재 선택 항목, 각 노드의 이름과 지연 측정 결과가 포함될 수 있습니다. 코어 버전에 따라 일부 필드의 이름이나 표시 방식이 다르므로, 스크립트는 필요하지 않은 필드에 의존하지 않도록 작성하는 것이 좋습니다.

엔드포인트용도요청 방식
/version코어 버전과 API 연결 확인GET
/proxies프록시 그룹, 현재 선택 노드 조회GET
/proxies/{name}특정 그룹 또는 노드의 상세 상태 조회GET
/proxies/{name}프록시 그룹의 선택 노드 변경PUT
/proxies/{name}/delay특정 노드의 테스트 URL 지연 측정GET
/connections현재 활성 연결과 사용 중인代理 그룹 확인GET

노드 이름은 YAML의 name 값과 정확히 일치해야 합니다. 한글, 공백, 괄호가 포함된 이름도 사용할 수 있지만 URL 경로에 직접 넣을 때는 인코딩 문제가 생길 수 있습니다. 자동화용 노드는 영문과 숫자, 하이픈 중심으로 이름을 정하면 운영이 편합니다. 또한 실제 전환 대상은 개별 노드가 아니라 보통 proxy-groups에 정의한 선택 그룹입니다.

프록시 목록 조회
curl -s \
  -H "Authorization: Bearer $CLASH_SECRET" \
  http://127.0.0.1:9090/proxies | jq '.proxies["Auto-Select"]'

특정 노드의 지연을 직접 확인하려면 테스트 URL과 제한 시간을 쿼리로 전달합니다. 예를 들어 다음 요청은 해당 노드로 테스트 URL에 연결하여 밀리초 단위의 지연을 측정합니다. 테스트 URL은 너무 큰 파일이 아니라 HTTP 상태 코드만 빠르게 반환하는 주소가 적합합니다.

노드별 지연 측정
curl -sG \
  -H "Authorization: Bearer $CLASH_SECRET" \
  --data-urlencode "timeout=5000" \
  --data-urlencode "url=https://www.gstatic.com/generate_204" \
  "http://127.0.0.1:9090/proxies/node-jp-01/delay"

지연 시간이 낮다고 해서 항상 좋은 노드는 아닙니다. 테스트 URL에만 빠르고 실제 서비스 연결은 실패할 수 있으며, 일시적으로 빠른 노드가 곧 안정적인 노드라는 뜻도 아닙니다. 따라서 자동 전환 조건에는 지연 임계값, 연속 실패 횟수, 최소 재검사 간격을 함께 넣는 것이 안전합니다.

API로 프록시 그룹 전환하기

프록시 그룹의 선택 노드를 변경할 때는 그룹 이름을 URL에 넣고 PUT 요청의 JSON 본문에 name을 전달합니다. 예를 들어 그룹 이름이 Auto-Select이고 대상 노드가 node-jp-01이라면 다음과 같이 요청합니다.

선택 노드 변경 요청
curl -X PUT \
  -H "Authorization: Bearer $CLASH_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"name":"node-jp-01"}' \
  "http://127.0.0.1:9090/proxies/Auto-Select"

그룹 이름에 공백이나 특수 문자가 있으면 URL 인코딩을 적용해야 합니다. 전환 직후에는 다시 GET /proxies/Auto-Select를 호출하여 실제 선택 노드가 바뀌었는지 확인하세요. API가 성공 상태를 반환했더라도 해당 노드가 연결 가능한지 별도로 검증하는 습관이 좋습니다.

가장 간단한 YAML 구성은 Clash 자체의 헬스 체크 기능과 API 스크립트를 함께 사용하는 방식입니다. url-test는 가장 빠른 노드를 선택하고, fallback은 정해진 순서에 따라 사용 가능한 노드를 선택합니다. 운영 중인 그룹은 자동화 스크립트가 제어할 수 있도록 명확한 이름을 부여하세요.

자동화 대상 프록시 그룹
proxy-groups:
  - name: Auto-Select
    type: select
    proxies:
      - node-jp-01
      - node-us-01
      - node-sg-01
      - DIRECT

  - name: Health-Test
    type: url-test
    proxies:
      - node-jp-01
      - node-us-01
      - node-sg-01
    url: https://www.gstatic.com/generate_204
    interval: 120
    tolerance: 80

장애 감지와 자동 전환 스크립트

실전 스크립트는 다음 순서로 동작합니다. 먼저 관리 API가 응답하는지 확인하고, 현재 그룹의 선택 노드를 읽습니다. 그다음 후보 노드를 순서대로 테스트하여 제한 시간 안에 응답한 노드를 찾고, 현재 노드가 실패했거나 지연 시간이 기준을 넘었을 때만 전환합니다. 정상 상태에서 매 주기마다 노드를 바꾸지 않도록 전환 쿨다운과 연속 실패 카운터를 두는 것이 핵심입니다.

Python 자동 전환 예제
import os
import time
import requests

BASE = "http://127.0.0.1:9090"
SECRET = os.environ["CLASH_SECRET"]
GROUP = "Auto-Select"
TEST_URL = "https://www.gstatic.com/generate_204"
CANDIDATES = ["node-jp-01", "node-us-01", "node-sg-01"]
TIMEOUT_MS = 2500
MAX_DELAY_MS = 900
COOLDOWN_SECONDS = 300

HEADERS = {
    "Authorization": f"Bearer {SECRET}",
    "Content-Type": "application/json",
}

last_switch = 0

def get_group():
    response = requests.get(
        f"{BASE}/proxies/{GROUP}",
        headers=HEADERS,
        timeout=5,
    )
    response.raise_for_status()
    return response.json()

def test_node(node):
    response = requests.get(
        f"{BASE}/proxies/{node}/delay",
        params={"timeout": TIMEOUT_MS, "url": TEST_URL},
        headers=HEADERS,
        timeout=5,
    )
    if response.status_code != 200:
        return None
    return response.json().get("delay")

def switch_to(node):
    response = requests.put(
        f"{BASE}/proxies/{GROUP}",
        headers=HEADERS,
        json={"name": node},
        timeout=5,
    )
    response.raise_for_status()
    print(f"switched to {node}")

while True:
    try:
        current = get_group().get("now")
        delay = test_node(current) if current else None

        if delay is None or delay > MAX_DELAY_MS:
            for candidate in CANDIDATES:
                candidate_delay = test_node(candidate)
                if candidate_delay is not None and candidate_delay <= MAX_DELAY_MS:
                    if candidate != current and time.time() - last_switch > COOLDOWN_SECONDS:
                        switch_to(candidate)
                        last_switch = time.time()
                    break
    except requests.RequestException as error:
        print(f"API error: {error}")

    time.sleep(30)

위 예제는 이해하기 쉬운 기본형입니다. 운영 환경에서는 한 번의 실패만으로 즉시 전환하지 말고, 같은 노드에서 2~3회 연속 실패했을 때 장애로 판정하는 편이 안정적입니다. 반대로 장애 복구 후 바로 원래 노드로 돌아가면 두 노드 사이를 반복해서 오가는 플래핑이 발생할 수 있습니다. 복구 노드는 일정 시간 동안 안정적으로 응답한 뒤에만 다시 후보에 포함시키세요.

  • 타임아웃: 모바일 네트워크나 혼잡한 회선에서는 2초보다 3~5초가 현실적입니다.
  • 지연 기준: 평소 측정값의 평균에 200~400ms를 더하는 방식이 고정값보다 적합합니다.
  • 연속 실패: 일시적인 패킷 손실을 장애로 오인하지 않도록 최소 2회 이상 확인합니다.
  • 전환 간격: 최소 3~5분의 쿨다운을 두어 세션이 불필요하게 끊기지 않게 합니다.
  • 로그 기록: 시간, 현재 노드, 테스트 지연, 전환 이유를 기록하면 장애 분석이 쉬워집니다.
장애 판정은 여러 신호를 조합하세요: 지연 시간이 높다는 이유만으로 전환하면 테스트 서버의 일시적인 문제에 반응할 수 있습니다. API 오류, 테스트 타임아웃, 실제 서비스의 HTTP 실패를 별도로 기록하고, 두 가지 이상의 신호가 일정 시간 지속될 때만 노드를 바꾸는 방식이 가장 안정적입니다.

운영 점검과 문제 해결

API 자동화가 작동하지 않을 때는 먼저 네트워크 규칙이 아니라 컨트롤러 자체를 점검해야 합니다. curl/version을 호출하여 포트가 열려 있는지 확인하고, 응답이 401이면 시크릿 또는 Bearer 헤더가 잘못된 것입니다. 404가 반환되면 오래된 코어에서 해당 엔드포인트를 지원하지 않거나 URL의 그룹 이름이 잘못되었을 가능성이 있습니다. 400은 JSON 본문, 노드 이름, 그룹 유형을 확인해야 합니다.

  • 연결 거부: external-controller 주소와 포트, 코어 재시작 여부, 운영체제 방화벽을 확인합니다.
  • 401 Unauthorized: Authorization: Bearer 형식과 시크릿의 앞뒤 공백을 확인합니다.
  • 404 Not Found: URL 인코딩 여부와 실제 프록시 그룹 이름을 확인합니다.
  • 전환했지만 트래픽이 그대로임: 실제 규칙이 다른 프록시 그룹을 참조하는지 확인합니다.
  • 지연 측정 실패: 테스트 URL이 해당 노드에서 차단되었을 수 있으므로 다른 HTTPS 엔드포인트를 사용합니다.

또한 API 스크립트와 Clash의 내장 자동 선택 기능이 같은 그룹을 동시에 제어하지 않도록 하세요. 예를 들어 스크립트가 Auto-Select를 수동으로 변경하는 동안 해당 그룹 자체가 url-test로 동작하면 두 로직이 서로 다른 결정을 내릴 수 있습니다. 스크립트가 최종 결정을 맡는 그룹은 select로 두고, 별도의 url-test 그룹은 후보 상태를 확인하는 용도로 분리하면 충돌을 줄일 수 있습니다.

마지막으로 API 시크릿을 코드에 직접 저장하지 말고 환경 변수나 운영체제의 비밀 저장소에서 읽으세요. 자동 전환은 편리하지만 잘못된 노드 이름 하나만으로도 모든 요청이 DIRECT로 빠지거나, 반복 전환으로 활성 연결이 끊길 수 있습니다. 작은 후보 목록과 명확한 로그부터 시작한 뒤, 실제 사용 패턴에 맞춰 테스트 주기와 임계값을 조정하면 Clash API를 안정적인 장애 대응 도구로 활용할 수 있습니다.

시작하기

Clash로 트래픽을 완전히 제어하세요

Windows, macOS, Linux, Android, iOS 지원. 유연한 규칙, 간단한 설정.

무료 다운로드 설정 가이드 보기 →