1. 먼저 증상을 세 가지로 나눕니다
첫째, 아예 소켓이 안 붙어 브라우저나 패널 앱이 곧바로 connection refused 를 띄우는 경우입니다. 이는 커널이 해당 주소·포트에서 듣고 있지 않거나, 방화벽이 로컬 루프백이 아닌 경로를 막는 경우가 많습니다. 둘째, 연결은 되는데 401·403처럼 인증에서 막히는 경우입니다. 이때는 secret 이 빠졌거나, 패널에 입력한 토큰이 구성과 다릅니다. 셋째, 페이지 뼈대는 뜨지만 API 호출만 실패해 스피너가 돌거나 노드 목록이 비는 경우입니다. 이때는 혼동하기 쉬운 프록시 포트와 external-controller 포트가 뒤바뀌었는지, 브라우저가 HTTPS만 강제하는지도 같이 봅니다. 처음부터 “전부 고장”으로 보기보다 위 세 갈래로 나누면 다음 단계에서 시간을 덜 태웁니다.
2. external-controller가 하는 일
external-controller 는 커널 밖의 패널·스크립트가 상태를 읽고 정책을 바꾸도록 열어 두는 REST 관문입니다. 프록시로 트래픽을 넘기는 port 나 mixed-port 와 목적이 완전히 다릅니다. 웹 UI는 보통 여기에 HTTP로 붙어 /proxies ·/rules 같은 엔드포인트를 호출합니다. 그래서 구독이 정상이어도 이 관문이 꺼져 있으면 패널만 죽은 것처럼 보입니다. 설정 파일에서 이 블록이 주석 처리되어 있거나, 클라이언트 업데이트 후 새 프로필로 덮이면서 빠진 사례도 흔합니다. “커널 프로세스는 도는데 패널만 안 된다” 면 우선 이 줄이 살아 있는지부터 확인하는 것이 좋습니다.
3. bind-address: 127.0.0.1과 0.0.0.0의 차이
bind-address 가 127.0.0.1 이면 같은 기기의 루프백에서만 접속이 가능합니다. 호스트 OS에서 돌아가는 Yacd 탭은 대개 이 경우에 열려야 정상입니다. 반면 휴대폰이나 다른 PC에서 http://데스크톱-LAN-IP:9090 으로 접속하려면, 컨트롤러가 최소한 그 LAN 인터페이스에 바인딩되어 있어야 합니다. 예시로 0.0.0.0 은 IPv4 모든 인터페이스에서 듣는 의미로 자주 쓰이지만, 대가는 공유기 뒤라도 같은 망의 다른 클라이언트가 스캔할 수 있다는 점입니다. IPv6 전용 환경에서는 :: 계열과의 조합이 필요할 때가 있어, 지금 쓰는 스택이 v4 위주인지도 같이 확인하세요. “왜 내 PC만 되고 옆 PC는 안 되나” 는 거의 항상 이 단계에서 설명됩니다.
4. 포트 번호: 9090이 맞는지, 충돌은 없는지
문서·예제에서 9090 이 기본으로 자주 쓰이지만, 반드시 고정은 아닙니다. 설정에 9090 이 아닌 값이 들어 있는데 패널 주소만 예전 습관으로 적어 두면 끝없이 거부됩니다. 또 다른 서비스가 같은 포트를 쓰면 커널이 바인딩에 실패하고 조용히 떨어지기도 합니다. 운영체제 도구로 해당 포트의 리스너를 확인해 보세요. Windows에서는 netstat 계열, macOS·Linux에서는 lsof 나 ss 가 도움이 됩니다. 포트를 바꿀 때는 설정·북마크·패널에 저장한 Base URL을 한꺼번에 맞춰야 합니다. 한 곳만 고치면 “가끔 되고 가끔 안 된다” 같은 표현으로 남습니다.
5. secret과 Authorization 헤더
보안을 위해 컨트롤러에는 secret 이 붙는 구성이 일반적입니다. 패널 설정 화면의 API 주소 옆 토큰 칸과 설정 파일의 값이 한 글자라도 다르면 401이 납니다. REST 클라이언트로 직접 호출할 때는 Authorization: Bearer … 형태가 많으니, 패널이 보내는 헤더 이름도 같이 확인합니다. secret을 비워 두면 편하지만, LAN에 포트를 넓혀 둔 상태에서는 위험이 커집니다. 테스트를 끝낸 뒤에는 의미 있는 무작위 문자열로 바꾸고, 스크린숏·레포에 노출되지 않게 관리하세요. “브라우저만 새로고침하면 된다” 식으로 넘어가기 어려운 증상일수록 인증 줄부터 의심하는 편이 빠릅니다.
6. curl로 REST API를 빠르게 확인
패널 앱 전에 터미널에서 최소 한 번 직접 두드려 보면 원인이 갈립니다. 예시는 반드시 본인의 포트·토큰으로 바꿉니다.
# Replace PORT and SECRET; use empty -H line only if your profile has no secret
curl -sS -H "Authorization: Bearer SECRET" "http://127.0.0.1:PORT/version"
루프백에서 이 응답이 오면 컨트롤러는 살아 있는 것입니다. 같은 명령을 다른 기기에서 실행할 때는 127.0.0.1 대신 서버의 LAN IP를 넣고, 그 전에 바인딩이 LAN을 받도록 설정됐는지 다시 봅니다. TLS를 쓰는 커스텀 리스슨은 이 글의 기본 전제와 다를 수 있으니, 운영 중인 템플릿의 스킴(http vs https)도 함께 맞춥니다.
7. 설정 파일에서 흔한 형태
실제 키 이름은 이용 중인 커널 버전과 템플릿에 따라 조금씩 다릅니다. 아래는 개념 정렬용 예시이며, 주석은 영어로만 적습니다.
# Example skeleton — verify keys against your Mihomo / Meta docs
external-controller: 0.0.0.0:9090
secret: "replace-with-strong-token"
# Optional explicit bind (names vary by release; check your reference config)
# external-controller-pipe: \\.\pipe\clash-meta
여기서 external-controller 한 줄이 빠지거나 들여쓰기가 깨져 상위 키 아래로 안 들어간 경우, 파서가 조용히 무시하는 일이 생깁니다. 큰 규칙 파일을 붙여 넣다가 중간에 duplicate 키가 생겨 나중 줄이 이기는 경우도 있으므로, 실제 로드된 런타임 설정 화면이 있는 클라이언트에서는 그쪽 표시와 파일을 교차 확인하세요.
8. 전용 GUI 클라이언트를 쓸 때 점검할 곳
Clash Verge Rev 같은 래퍼는 UI 스위치와 파일 기반 설정이 동시에 존재합니다. UI에서 API를 꺼 두었는데 파일만 고치면 반영이 안 되거나, 반대로 일시 토글이 파일을 덮어쓸 수 있습니다. 외부 패널만 쓰려면 “외부에서 컨트롤러 접근 허용” 류의 옵션과 실제 바인딩 주소가 일치하는지 봅니다. 내장 패널을 켠 채 외부 Yacd를 또 붙이려다 포트가 겹친 사례도 있습니다. 캐시된 오래된 API 주소가 브라우저 Service Worker에 남으면 설정을 바꿔도 옛 주소로 요청이 가기도 하니, 프라이빗 창으로 한 번 검증해 보는 것도 비용이 작습니다.
9. Docker·WSL·VM에서 한 단 더 건너뛰는 경우
컨테이너 안에서 커널을 돌리면 호스트 브라우저의 127.0.0.1 과 컨테이너 루프백이 다릅니다. 포트 퍼블리시를 9090:9090 처럼 열었는지, WSL2에서는 Windows 쪽에서 접속할 IP가 무엇인지 정리가 필요합니다. 가상머신 브리지 모드라면 게스트 OS의 주소로 접속해야 합니다. 이런 환경에서는 bind-address 를 조정하기 전에, 네트워크 경로가 먼저 맞는지 traceroute 대신 간단히 ping 과 위 REST API 호출로 경계를 나눕니다. 하이퍼바이저 보안 스택이 lo 트래픽만 다르게 취급하는 경우도 있어, 동일 증상이라도 물리 기기와 다른 점을 염두에 두세요.
10. OS 방화벽과 third-party 필터
바인딩이 LAN으로 열려도 OS 방화벽이 들어오는 TCP를 막으면 겉으로는 refused와 비슷하게 보입니다. 기업 단말에는 추가 보안 에이전트가 포트를 가로채기도 합니다. 임시로 규칙을 추가할 수 있다면, 신뢰할 수 있는 출처에서 다운로드한 패널만 대상으로 최소 범위를 여는 편이 낫습니다. 반대로 불필요하게 전체 공용 프로필을 연 뒤 forget 하는 것은 추적이 어렵습니다. 테스트가 끝나면 닫을 수 있는지, 감사 로그에 남는지까지 운영 정책과 맞춰 주세요.
11. LAN 공개와 최소 권한 습관
0.0.0.0 으로 열면 편하지만, 카페·회사처럼 신뢰하기 어려운 망에서는 피하는 것이 좋습니다. 꼭 필요하면 강한 secret ·짧은 테스트 시간·방화벽 화이트리스트를 세트로 생각하세요. 리버스 프록시나 SSH 터널로 좁혀 접속하는 방법도 있습니다. 이 글은 기술 절차에 초점을 두지만, 노출 면적을 줄이는 설계가 곧 안정성과도 연결됩니다. 공용 와이파이에서 디버그 목적으로 포트를 넓혔다가 그대로 두는 습관이 가장 위험한 패턴 중 하나입니다.
12. mixed-port·socks-port와 혼동하지 않기
사용자 트래픽이 지나가는 포트에 웹 패널 주소를 넣으면 전혀 다른 오류가 납니다. 증상은 “연결은 되는 것 같은데 JSON이 아니다” 쪽으로 기울 수 있습니다. 문서에서 external-controller 줄을 다시 찾아, 숫자가 mixed-port 와 다른지 확인하세요. 교육 자료마다 스크린샷 포트가 달라 헷갈리기 쉬우니, 지금 돌아가는 프로세스의 실제 listening 표를 기준으로 삼는 습관이 안전합니다.
13. 브라우저 혼합 콘텐츠·확장 프로그램
HTTPS 페이지 안에서 HTTP API를 직접 부르면 브라우저가 막을 수 있습니다. 패널을 로컬 파일로 열거나 별도 탭에서 순수 HTTP로 접속해 보세요. 광고 차단·프라이버시 확장이 로컬호스트 요청까지 건드리는 경우도 있어, 확장을 끈 임시 프로필로 비교 실험을 권합니다. 이런 층을 제외하고도 API가 JSON으로 돌아오면 설정 문제에 집중할 수 있습니다.
14. 실측 체크리스트로 마무리
운영 순서를 한 번에 적으면 다음과 같습니다. 커널이 실제로 요청한 포트에서 듣는지 확인한다. bind-address 가 기대한 인터페이스와 맞는지 본다. secret 이 패널·파일·환경 변수에 일관적인지 본다. curl 로 REST API 를 루프백과 LAN 각각에서 검증한다. GUI 클라이언트 토글과 디스크 파일이 충돌하지 않는지 본다. Docker·WSL이면 포트 매핑을 추가로 본다. 이 리듬은 범용 트러블슈팅 글과 달리 “외부 콘솔” 축만 빠르게 닫기 위한 것입니다. 반복 장애가 남으면 문제 해결 가이드의 다른 머리를 이어서 읽으면 DNS·규칙 쪽으로 자연스럽게 확장됩니다.
15. 정리
Yacd ·metacube 같은 웹 UI는 Clash 의 external-controller 위에서 돌아갑니다. 주소·포트·bind-address ·secret 이 한 세트로 맞아야 하며, 127.0.0.1:9090 과 LAN의 차이를 이해하면 “한 기기만 된다” 류 증상이 바로 줄어듭니다. REST API 를 직접 호출해 살아 있음을 확인한 뒤 패널을 다시 연결하면 원인 분기가 명확해집니다. 설정을 자주 바꾼다면 GUI와 파일의 이중 관리만큼 주의할 부분도 많지 않습니다.
규칙·DNS·연결 로그를 한 화면에서 다루는 Mihomo 호환 클라이언트는 이런 점검 후에도 운영이 단순합니다. 범용 툴에 비해 외부 콘솔과의 궁합이 문서화되어 있는 편이라 학습 비용도 상대적으로 낮습니다. → Clash 무료 다운로드 페이지에서 클라이언트를 받아 동일한 external-controller 절차를 적용해 보세요.