Clash 구독 형식 변환: YAML·Base64 노드 링크·sing-box 설정
Clash YAML, Base64 노드 구독, sing-box 설정의 차이와 클라이언트별 지원 형식, 로컬 변환 시 확인할 항목과 보안 주의사항을 정리합니다.
먼저 콘텐츠 형식을 확인한 뒤 가져오기 메뉴 선택
구독 링크는 콘텐츠를 가져오는 주소이지, 형식의 이름이 아닙니다. 같은 주소라도 요청 매개변수나 클라이언트의 User-Agent에 따라 다른 콘텐츠를 반환할 수 있습니다. 파일 이름이 .yaml로 끝나더라도 실제 응답이 Clash 설정이라고 단정할 수는 없습니다. 가져오기에 실패하면 파일 확장자를 계속 바꾸기보다 실제 응답을 로컬에서 먼저 확인하세요.
세 가지 층위를 구분해야 합니다. Base64는 인코딩 방식이고, ss:// 같은 URI는 노드를 표현하는 방식이며, Clash YAML과 sing-box JSON은 각각의 코어에서 사용하는 설정 구조입니다. 디코딩해 노드 목록을 얻었다고 해서 트래픽 분기 규칙, DNS, 수신 대기 포트가 포함된 완전한 설정이 만들어지는 것은 아닙니다.
| 콘텐츠 형식 | 로컬에서 확인할 특징 | 일반적으로 포함되는 내용 |
|---|---|---|
| Clash 전체 YAML 설정 | 최상위에 proxies, proxy-groups, rules가 자주 포함됩니다 | 노드, 프록시 그룹, 규칙. DNS와 TUN 설정이 포함되기도 합니다 |
| Clash 노드 프로바이더 YAML | 일반적으로 최상위 proxies: 목록이 핵심입니다 | proxy-providers에서 참조하는 노드 모음 |
| 노드 URI 목록 | 여러 줄의 ss://, vmess://, trojan:// 등 | 노드 연결 매개변수. 일반적으로 완전한 트래픽 분기 정책은 포함되지 않습니다 |
| Base64 구독 | 문자, 숫자, 인코딩 기호가 이어진 형태로 보이며 디코딩한 뒤 확인합니다 | 흔히 노드 URI 목록으로 구성되지만 실제 형식은 디코딩 결과로 판단해야 합니다 |
| sing-box JSON | 최상위에 inbounds, outbounds, route가 자주 포함됩니다 | 인바운드, 아웃바운드, 라우팅 및 선택적 DNS 설정 |
Clash YAML: 전체 설정과 노드 프로바이더
YAML을 파싱할 수 있다고 해서 프록시가 바로 작동하는 것은 아닙니다
YAML은 데이터를 표현하는 형식이고, mihomo는 필드 형식과 노드 매개변수, 참조 관계도 검사합니다. 들여쓰기에는 탭이 아닌 공백을 사용하세요. 포트는 필드 요구사항에 맞게 숫자로 입력해야 합니다. 특수 문자가 포함된 이름, 비밀번호, URL은 따옴표로 감싸면 #이 주석으로 처리되거나 콜론으로 구조가 바뀌는 일을 막을 수 있습니다.
아래는 구조를 파악하기 위한 직결 예시이며 프록시 노드는 포함하지 않습니다. 로컬 127.0.0.1:7890에서 수신하고 규칙 모드를 활성화한 뒤, 최종 트래픽을 ‘출구 선택’이라는 프록시 그룹으로 보냅니다. 그룹에는 DIRECT만 있으므로 실행 후에도 직접 연결됩니다. 이 예시는 클라이언트의 시스템 프록시나 TUN을 자동으로 활성화하지 않습니다.
mixed-port: 7890
allow-lan: false
bind-address: 127.0.0.1
mode: rule
log-level: info
proxies: []
proxy-groups:
- name: 출구 선택
type: select
proxies:
- DIRECT
rules:
- MATCH,출구 선택
프로바이더 파일은 메인 설정에서 참조해야 합니다
노드 프로바이더에는 보통 proxies 목록만 들어 있습니다. 메인 설정에서 proxy-providers로 소스, 업데이트 간격, 상태 확인을 정의한 다음, 프록시 그룹의 use로 프로바이더 이름을 참조합니다. 프로바이더 파일만 전체 설정처럼 가져오면 노드는 보여도 예상한 프록시 그룹이 없는 상황이 생길 수 있습니다.
- 참조 확인: 규칙에서 사용하는 프록시 그룹 이름이 실제로 있어야 합니다. 프록시 그룹에 지정한 노드 이름이나 프로바이더 이름도 일치하는지 확인하세요.
- 이름 중복 확인: 노드 이름이 중복되면 선택과 참조가 모호해집니다. 변환 과정에서 이름을 바꿨다면 관련 참조도 함께 수정하세요.
- 코어 확인: Clash 호환 설정이라고 해서 모든 구버전 Clash 코어에서 지원되는 것은 아닙니다. 일부 프로토콜이나 확장 필드에는 mihomo가 필요할 수 있습니다. 실제 지원 여부는 클라이언트에서 사용하는 코어를 기준으로 확인하세요.
Base64: 디코딩은 프로토콜 변환이 아닙니다
Base64는 구독 정보를 숨기지 않으며 누락된 필드를 자동으로 채우지도 않습니다. 흔히 여러 노드 URI를 줄바꿈으로 연결한 뒤 전체 텍스트를 한 번 인코딩합니다. URI 자체에 Base64 문자열이 포함된 경우도 있습니다. 따라서 바깥쪽 데이터를 디코딩한 다음에는 각 줄의 프로토콜부터 확인하세요. 텍스트처럼 보일 때까지 무작정 반복해서 디코딩하지 마세요.
Python 3으로 로컬에서 바깥쪽 콘텐츠 확인하기
가져온 인코딩 본문을 subscription.txt로 저장하고 아래 스크립트는 decode_subscription.py로 저장한 다음 python3 decode_subscription.py를 실행하세요. 스크립트는 일반 Base64와 URL-safe 문자표를 모두 처리하고 끝부분의 패딩을 보완합니다. 디코딩만 수행하며 노드에 연결하거나 설정을 변환하지 않고, 노드 내용을 터미널에 출력하지도 않습니다.
from pathlib import Path
import base64
import binascii
source = Path("subscription.txt").read_text(encoding="utf-8-sig")
compact = "".join(source.split())
if not compact:
raise SystemExit("입력 내용이 비어 있습니다")
compact += "=" * (-len(compact) % 4)
try:
raw = base64.b64decode(compact, altchars=b"-_", validate=True)
text = raw.decode("utf-8")
except (ValueError, binascii.Error, UnicodeDecodeError):
raise SystemExit("UTF-8 Base64 텍스트로 디코딩할 수 없습니다. 원본 응답을 먼저 확인하세요")
with Path("decoded.txt").open("x", encoding="utf-8") as output:
output.write(text)
print("decoded.txt에 저장했습니다. 콘텐츠 형식을 로컬에서 확인하세요")
기존 decoded.txt 파일을 덮어쓰지 않도록 배타적 쓰기 모드를 사용합니다. 파일이 이미 있다면 먼저 보존할 필요가 있는지 확인하세요. 디코딩한 파일에도 민감한 정보가 남아 있으므로 공개 코드 저장소에 올리거나 단체 채팅방에 공유하지 마세요.
URI 내부도 프로토콜에 맞게 파싱해야 합니다
ss://는 표현 방식이 여러 가지이며 사용자 정보가 Base64로 인코딩되어 있을 수 있고 플러그인 매개변수가 붙기도 합니다. 첫 번째 콜론이나@만 기준으로 나눠서는 안 됩니다.vmess://는 인코딩된 JSON 형식이 흔하지만 필드를 확인해야 합니다. 임의의 범용 노드 문자열처럼 취급하면 안 됩니다.trojan://,vless://등의 링크는 쿼리 매개변수로 TLS, 전송 계층, 경로 정보를 전달하는 경우가 많습니다. URL 퍼센트 디코딩도 필드 경계를 고려해 처리해야 합니다.- URI 조각에 표시되는 이름은 대개 연결 매개변수가 아닙니다. 노드 이름을 바꾸면 구분하기는 쉬워지지만, 잘못된 서버 주소나 인증 정보가 수정되지는 않습니다.
sing-box JSON과 Clash YAML의 변환 범위
두 형식 모두 프록시 연결을 표현할 수 있지만 설정 모델은 서로 다릅니다. Clash는 일반적으로 노드, 프록시 그룹, 순서가 있는 규칙으로 출구를 구성합니다. sing-box는 tag가 지정된 아웃바운드와 라우팅 참조로 트래픽을 구성합니다. proxies를 outbounds로 이름만 바꿔도 유효한 sing-box 설정이 되지는 않습니다.
아래는 sing-box에서 직접 연결하는 구조의 예시입니다. HTTP와 SOCKS가 127.0.0.1:7890을 함께 사용하고 최종 라우팅은 direct-out으로 향합니다. 필드를 비교하기 위한 별도 예시이므로 위의 mihomo 예시와 같은 수신 주소 및 포트를 동시에 사용할 수 없습니다.
{
"inbounds": [
{
"type": "mixed",
"tag": "mixed-in",
"listen": "127.0.0.1",
"listen_port": 7890
}
],
"outbounds": [
{
"type": "direct",
"tag": "direct-out"
}
],
"route": {
"final": "direct-out"
}
}
| 변환 항목 | 처리해야 할 차이 |
|---|---|
| 노드 연결 | 프로토콜, 주소, 포트, 인증, TLS, 전송 계층 매개변수를 각각 매핑합니다 |
| 프록시 선택 | Clash 프록시 그룹과 sing-box의 선택형 아웃바운드는 구조와 옵션이 완전히 같지 않습니다 |
| 트래픽 분기 규칙 | 규칙 문법, 동작, 규칙 세트 형식이 다르므로 그대로 복사할 수 없습니다 |
| DNS | 서버 정의, DNS 라우팅, Fake-IP, 부트스트랩 DNS를 별도로 구성해야 합니다 |
| 트래픽 가로채기 | 시스템 프록시는 클라이언트나 시스템에서 설정합니다. TUN에는 권한, 라우팅, 플랫폼별 차이도 영향을 줍니다 |
설정과 함께 버전도 기록하세요
sing-box의 DNS 및 라우팅 필드는 버전이 바뀌면서 여러 차례 변경됐습니다. 예를 들어 1.12.0에서는 새로운 DNS 서버 설정 형식이 도입됐습니다. 따라서 특정 클라이언트에서 가져올 수 있었던 구버전 설정이 코어를 업데이트한 뒤에도 계속 작동한다고 볼 수 없습니다. 변환 전에 소스 클라이언트 코어 버전, 대상 코어 버전, 변환기 버전을 기록하고 대상 버전 문서에 따라 설정을 생성하세요. 앱 화면에 표시된 버전만 기록해서는 안 됩니다.
클라이언트의 형식 지원: 가져오기 기능과 코어 구분하기
‘클라이언트가 특정 형식을 지원한다’는 말은 가져오기나 변환 기능이 내장되어 있다는 뜻일 수 있으며, 반드시 하위 코어가 해당 형식을 직접 읽는다는 의미는 아닙니다. mihomo 코어를 사용하는 클라이언트는 대체로 Clash YAML을 중심으로 작동합니다. 일부 클라이언트는 노드 링크를 설정으로 정리할 수 있지만 Base64 전체를 받아들이는지, 규칙과 DNS를 보존하는지는 가져오기 메뉴에 따라 다릅니다. sing-box 코어는 주로 자체 JSON 설정을 읽으며, GUI 클라이언트가 별도의 구독 관리 기능을 제공하기도 합니다.
- 먼저 가져오기 메뉴의 용도를 확인하세요: ‘URL에서 설정 가져오기’, ‘노드 추가’, ‘노드 프로바이더 가져오기’, ‘로컬 파일 가져오기’를 구분해야 합니다. 단일 노드 추가 메뉴는 일반적으로 완전한 규칙 구성을 자동으로 만들지 않습니다.
- 그다음 가져오기 미리보기를 확인하세요: 노드 수, 프로토콜 종류, 프록시 그룹 이름을 확인합니다. 원본 파일에 노드가 12개인데 결과에 9개만 표시된다면 바로 활성화하지 말고 누락된 3개 노드부터 확인하세요.
- 마지막으로 실제 활성화된 설정을 확인하세요: 구독 캐시, 사용자 오버라이드, 클라이언트가 생성한 설정은 서로 다른 파일일 수 있습니다. 화면에서 수정한 내용이 다음 구독 업데이트 후에도 유지되는지 별도로 확인하세요.
클라이언트를 다시 선택해야 한다면 운영체제별 다운로드 항목을 다운로드 센터에서 확인하세요. 가져오기와 처음 트래픽을 라우팅하는 방법은 구독 가져오기 가이드를 참고하세요. 클라이언트 이름만 보고 모든 Clash 확장 필드를 지원한다고 판단하지 마세요.
로컬 변환 및 검증: 다섯 단계로 문제 좁히기
1. 원본 파일과 대상 버전 저장
편집하지 않을 원본 파일을 보관하고 변환 결과는 별도로 저장하세요. 가져온 시각, 콘텐츠 형식, 대상 코어 버전을 기록합니다. 구독 주소에 접근 토큰이 포함되어 있다면 기록에는 용도만 적고 전체 주소는 공유 문서에 남기지 마세요.
2. 노드를 먼저 변환하고 정책은 나중에 이전
먼저 서버, 포트, 비밀번호 또는 UUID, 암호화 방식, 전송 매개변수를 확인한 다음 그룹과 규칙을 처리하세요. 대상 코어에서 표현할 수 없는 프로토콜은 실패 내역을 남겨야 하며 다른 프로토콜로 조용히 대체해서는 안 됩니다. 노드 목록만 받은 경우에는 변환기가 원래 없던 규칙까지 복원하리라 기대하지 말고 DNS와 트래픽 분기 정책을 별도로 설계하세요.
3. 대상 코어로 구문 검사
아래 명령은 코어 실행 파일이 현재 터미널 경로에 있다고 가정합니다. mihomo의 -t는 설정을 검사하고 sing-box의 check는 설정의 유효성을 확인합니다. Windows에서는 해당하는 .exe 파일 이름을 사용하세요. 검사를 통과해도 설정 형식이 허용된다는 뜻일 뿐, 서버에 연결되거나 인증에 성공한다는 의미는 아닙니다.
mihomo -t -f converted.yaml
sing-box check -c converted.json
4. 명시적 프록시로 노드부터 확인
시스템 설정의 영향을 줄이려면 먼저 TUN을 켜지 않은 상태에서 확인된 로컬 HTTP 프록시 포트로 테스트하세요. 아래 예시는 설정에 7890 포트가 있고 현재 선택된 정책이 테스트할 노드로 지정되어 있어야 합니다. 테스트 사이트는 요청 대상일 뿐 노드 서버가 아닙니다.
curl --proxy http://127.0.0.1:7890 --connect-timeout 10 --max-time 20 -I https://example.com/
이 명령이 성공해도 해당 요청이 로컬 프록시를 통해 처리됐다는 사실만 확인할 수 있습니다. 연결 패널이나 로그에서 최종 아웃바운드도 확인해야 합니다. DIRECT가 선택됐다면 프록시 노드가 정상 작동한다고 단정할 수 없습니다. 실패하면 포트 미수신, DNS 오류, TLS 핸드셰이크 실패, 원격 서버 인증 거부 순으로 원인을 확인하세요.
5. 시스템 프록시와 TUN을 차례로 확인
명시적 프록시 테스트가 정상이면 시스템 프록시를 켜고 브라우저를 테스트하세요. 시스템 프록시를 따르지 않는 앱의 트래픽까지 처리해야 할 때 TUN을 테스트합니다. TUN 권한, 라우팅, DNS 가로채기 문제는 구독 인코딩 문제와 별개입니다. 한 번에 한 계층만 변경해야 노드 변환과 트래픽 가로채기 중 어디에서 문제가 생겼는지 판단할 수 있습니다.
구독 정보와 변환 결과 보관하기
구독 URL을 온라인 변환 서비스에 입력하면 대개 해당 서비스가 원본 구독 정보와 노드 인증 정보를 가져올 수 있습니다. 브라우저에서 변환 페이지를 열었다는 사실만으로 처리가 로컬에서 이뤄진다고 볼 수는 없습니다. 오프라인 처리가 필요하면 원격 서비스에 의존하지 않는지 확인된 로컬 도구를 사용하고, 규칙 템플릿이나 원격 프로바이더 같은 추가 리소스가 여전히 인터넷에 연결되는지도 확인하세요.
- 로컬 인터페이스로 제한: 변환 서비스를 이 컴퓨터에서만 사용할 경우
127.0.0.1에서 수신하도록 설정하세요.0.0.0.0이나 공용 네트워크 포트에 직접 노출하지 마세요. - 파일 접근 권한 제한: Linux 또는 macOS에서는
chmod 600 subscription.txt decoded.txt converted.yaml을 사용해 기존 파일의 접근을 제한할 수 있습니다. Windows에서는 파일 보안 권한을 설정하세요. - 민감 정보 제거 후 문제 해결: 오류 정보를 공유할 때 프로토콜, 필드 이름, 오류 위치는 남기되 구독 토큰, 비밀번호, UUID, 공개해서는 안 되는 서버 정보는 삭제하세요.
- 유출 후 인증 정보 교체: Base64 텍스트도 평문 설정처럼 보호해야 합니다. 구독 링크가 공개됐다면 게시물을 삭제하는 데 그치지 말고 제공업체 관리 화면에서 링크나 관련 인증 정보도 재설정하세요.
변환 완료 기준은 ‘파일이 열린다’가 아닙니다. 대상 코어 검사를 통과하고, 주요 필드가 보존되며, 프록시 그룹 참조가 온전하고, 실제 연결이 예상한 아웃바운드를 사용하는지 확인해야 합니다. 제공업체가 대상 형식으로 직접 생성한 구독을 우선 사용하세요. 형식 변환이 꼭 필요하다면 원본을 보관하고 버전을 기록한 뒤 단계별로 검증하면 구독 업데이트 후 반복되는 문제 해결 작업을 줄일 수 있습니다.