구독 형식: 노드 목록과 전체 설정 구분하기
‘구독 링크’는 콘텐츠를 가져오는 경로를 뜻하며 파일 형식을 가리키지는 않습니다. 같은 주소라도 요청 매개변수나 헤더, 서버 설정에 따라 서로 다른 콘텐츠가 반환될 수 있습니다. Clash 클라이언트에 가져올 수 있는지 판단하려면 URL이 .yaml로 끝나는지만 볼 게 아니라 실제 응답을 확인해야 합니다.
일반적인 입력 형식으로는 한 줄에 하나씩 나열된 노드 공유 링크, 링크 목록 전체를 Base64로 인코딩한 텍스트, 노드와 라우팅 설정을 포함한 전체 설정 파일이 있습니다. Base64는 인코딩 방식이지 암호화나 프록시 프로토콜이 아닙니다. 디코딩 방법을 아는 사람은 서버 주소와 인증 정보를 복원할 수 있으므로 인코딩된 구독도 안전하게 관리해야 합니다.
| 콘텐츠 유형 | 일반적인 구조 | 일반적으로 포함되는 항목 | 이것만으로 단정할 수 없는 점 |
|---|---|---|---|
| 노드 링크 목록 | ss://, vmess://, vless:// 등(한 줄에 하나씩) |
노드 주소, 인증 정보, 일부 전송 매개변수 | 정책 그룹, DNS, 분기 규칙 설정 여부 |
| Base64 구독 | 목록 전체를 인코딩한 텍스트 | 대개 디코딩된 링크 목록과 동일한 내용 | 특정 클라이언트가 모든 프로토콜을 바로 인식하는지 여부 |
| Clash YAML | proxies、proxy-groups、rules |
노드, 정책 그룹, 규칙, 필요에 따라 DNS 등 설정 | 모든 Clash 계열 코어가 모든 필드를 지원하는지 여부 |
| sing-box JSON | inbounds、outbounds、route |
인바운드, 아웃바운드, 라우팅, 필요에 따라 DNS 등 설정 | Clash YAML과 필드 단위로 완전히 대응하는지 여부 |
콘텐츠 확인: 응답을 살펴본 다음 Base64 디코딩하기
로그인 페이지와 오류 페이지부터 걸러내기
구독을 로컬에 내려받은 뒤 텍스트 편집기로 파일 앞부분을 확인하세요. <!DOCTYPE html>, 로그인 양식, 접근 제한 안내가 보이면 설정 파일이 아니라 웹페이지를 받은 것입니다. HTTP 상태 코드가 200이어도 콘텐츠가 올바르다는 보장은 없습니다. 구독 만료, 요청 차단, 계정 인증 필요 등의 이유로 HTML이 반환될 수 있습니다.
- 계정 관리 페이지에서 해당 클라이언트용 구독 경로를 선택했는지 확인하고 유효기간도 살펴보세요.
- 리디렉션 이후 실제로 반환된 콘텐츠를 확인해 오류 페이지를 변환기에 보내지 않도록 하세요.
- 이미
proxies:나 JSON 객체가 있다면 구조를 보고 용도를 판단하세요. Base64 디코딩을 중복해서 하지 마세요. - 콘텐츠가 실제 인코딩 텍스트일 때만 로컬에서 디코딩하세요. 토큰이 포함된 내용을 공개 온라인 디코더에 붙여 넣지 마세요.
로컬 디코딩 예시
아래 예시는 Python 3 표준 라이브러리로 로컬 파일 subscription.txt을 읽습니다. 일반 Base64와 URL 안전 Base64 문자를 모두 지원하고 끝부분의 패딩도 보완합니다. 인코딩 계층만 처리하며 노드 프로토콜은 분석하지 않습니다. 개인 터미널에서 실행하세요. 출력에 비밀번호가 포함될 수 있으므로 문의 티켓이나 공개 로그에 남기면 안 됩니다.
from pathlib import Path
import base64
raw = Path("subscription.txt").read_text(encoding="utf-8").strip()
compact = "".join(raw.split())
compact += "=" * (-len(compact) % 4)
decoded = base64.b64decode(compact, altchars=b"-_", validate=True)
print(decoded.decode("utf-8"))
디코딩에 실패했다고 해서 구독이 반드시 손상된 것은 아닙니다. 처음부터 인코딩되지 않은 콘텐츠일 수도 있습니다. UTF-8 텍스트를 얻었다면 한 줄씩 나열된 노드 링크인지 확인하세요. 목록 전체를 디코딩했다고 해서 링크 안쪽까지 다시 통째로 디코딩해야 하는 것은 아닙니다. 예를 들어 Shadowsocks 사용자 정보와 일부 VMess 공유 형식에는 각기 다른 인코딩 규칙이 있으므로 프로토콜 파서가 따로 처리해야 합니다.
변환 원리: 노드를 파싱한 뒤 대상 설정 생성하기
구독 변환은 YAML의 콜론을 JSON의 중괄호로 바꾸는 작업이 아닙니다. 변환기는 보통 원본 형식을 파싱해 프로토콜, 주소, 포트, 인증 정보, 전송 매개변수를 추출하고 내부 노드 모델을 만든 다음 대상 코어가 지원하는 필드로 출력합니다. 정책 그룹, 규칙, DNS, 인바운드 설정은 별도의 템플릿으로 추가되는 경우가 많습니다.
따라서 ‘변환 성공’은 출력 파일이 생성됐다는 뜻일 뿐, 노드 프로토콜이 작동하거나 원본 설정의 동작이 모두 유지된다는 뜻은 아닙니다. 전체 YAML에서 노드 링크를 추출하면 정책 그룹, 규칙 제공자, DNS 분기, TUN 설정 등 노드 이외의 정보는 보통 링크에 담을 수 없습니다. 이 링크로 JSON을 생성해도 누락된 정보가 자동으로 복구되지는 않습니다.
일반적인 변환 방향과 한계
- Base64 → Clash YAML: 먼저 링크를 디코딩하고 파싱해
proxies를 생성한 다음 필요한 정책 그룹과 규칙을 추가합니다. - Clash YAML → Base64: 보통 공유 링크로 표현할 수 있는 노드만 내보낸 뒤 링크 목록을 인코딩합니다. 설정 백업용으로 볼 수는 없습니다.
- Clash YAML → sing-box JSON: 노드 필드를 매핑해야 하며 라우팅과 DNS는 대상 버전에 맞게 다시 구성해야 합니다.
- sing-box JSON → Clash YAML: 아웃바운드 유형과 규칙 동작을 대응하는 기능이 있는지 확인해야 합니다. 사용자 지정 라우팅 로직은 직접 다시 구성해야 할 수도 있습니다.
원본 Clash와 Clash Meta(현재 mihomo로 불림)는 지원 범위가 다릅니다. 최신 프로토콜을 사용하는 설정이라면 해당 프로토콜을 지원하는 대상 코어를 명확히 선택하세요. 파일 확장자를 .yaml로 바꿔도 구형 코어에 VLESS, Reality 등의 새 기능이 추가되지는 않습니다. GUI 클라이언트마다 포함된 코어 버전이 다를 수 있으므로 실제 실행 중인 코어 버전을 기준으로 판단하세요.
필드 매핑: Shadowsocks 노드로 비교하기
아래 두 설정은 같은 예시 Shadowsocks 아웃바운드를 표현합니다. 서버 포트는 8388, 로컬 혼합 프록시 포트는 7890입니다. 도메인에는 예약된 .invalid 접미사를 사용했고 비밀번호는 예시 값이므로, 필드 이해를 위한 설정일 뿐 실제 인터넷 연결에는 사용할 수 없습니다. 두 설정 모두 TUN이나 시스템 프록시를 활성화하지 않습니다.
mihomo YAML: 노드, 선택 그룹, 기본 규칙
mixed-port: 7890
allow-lan: false
mode: rule
log-level: info
proxies:
- name: 예시 노드
type: ss
server: node.example.invalid
port: 8388
cipher: aes-128-gcm
password: "demo-only-password"
proxy-groups:
- name: 아웃바운드 선택
type: select
proxies:
- 예시 노드
- DIRECT
rules:
- MATCH,아웃바운드 선택
sing-box JSON: 인바운드, 아웃바운드, 최종 라우팅
{
"log": {
"level": "info"
},
"inbounds": [
{
"type": "mixed",
"tag": "mixed-in",
"listen": "127.0.0.1",
"listen_port": 7890
}
],
"outbounds": [
{
"type": "shadowsocks",
"tag": "예시 노드",
"server": "node.example.invalid",
"server_port": 8388,
"method": "aes-128-gcm",
"password": "demo-only-password"
},
{
"type": "direct",
"tag": "직접 연결"
},
{
"type": "selector",
"tag": "아웃바운드 선택",
"outbounds": ["예시 노드", "직접 연결"],
"default": "예시 노드"
}
],
"route": {
"final": "아웃바운드 선택"
}
}
여기서 cipher는 method에 대응하고, 노드 서버 포트 port는 server_port에 대응합니다. 노드 이름은 참조에 사용되는 tag로 바뀝니다. Clash의 내장 DIRECT는 이 JSON 예시에서 명시적인 direct 아웃바운드로 표현했습니다. 실제 구성은 사용 중인 sing-box 버전에서 검증해야 하며, 특히 DNS, TUN, 라우팅 동작처럼 계속 변경되는 설정 부분을 주의해서 확인하세요.
두 프로그램이 로컬의 같은 7890 리스닝 포트를 동시에 사용할 수는 없습니다. 비교 테스트를 하려면 둘 중 하나를 종료하거나 다른 설정의 포트를 7891로 바꾸고 테스트 도구의 프록시 주소도 함께 수정하세요. 그렇지 않으면 ‘포트가 이미 사용 중’이라는 오류를 구독 변환 실패로 오해할 수 있습니다.
변환 옵션: 대상, 필터, 템플릿 따로 확인하기
변환 서비스에는 통일된 매개변수 규격이 없습니다. subconverter 기반 배포 환경 중에는 target, url, include, exclude, config 등의 매개변수를 받는 곳도 있지만 이름이 같다고 의미까지 같은 것은 아닙니다. 먼저 서비스 운영자가 제공한 안내를 확인하고 다른 서비스의 변환 URL을 그대로 적용하지 마세요.
| 매개변수 유형 | 확인할 내용 | 자주 발생하는 결과 |
|---|---|---|
| 대상 형식 | 사용 중인 mihomo 또는 sing-box 버전을 명시적으로 지원하는지 | 대상을 잘못 고르면 지원되지 않는 프로토콜이나 필드가 제외됩니다 |
| 원본 구독 주소 | 중첩 URL을 서비스 요구 사항에 맞게 인코딩했는지 | 원본 주소의 &가 바깥쪽 매개변수 구분자로 처리됨 |
| 노드 필터 | 포함·제외 조건에 정규식을 사용하는지 | 필터 적용 후 노드가 하나도 남지 않아 정책 그룹 참조가 무효가 됨 |
| 설정 템플릿 | DNS, 규칙, 그룹 이름, 리스닝 포트를 덮어쓰는지 | 노드는 그대로지만 트래픽 경로와 DNS 조회 동작이 달라짐 |
| 이름 변경 및 정렬 | 이름 중복을 방지하고 참조도 함께 갱신하는지 | 중복 노드 이름이나 연결되지 않은 참조로 로드 오류가 발생함 |
중첩 URL은 공백만 감으로 치환하지 말고 URL 인코딩 도구를 사용해 매개변수 값 하나로 전달하세요. 생성된 결과 전체를 반복 인코딩하지도 마세요. 그러면 원본 주소의 %가 다시 %25로 바뀔 수 있습니다. 변환기가 빈 목록을 반환하면 필터 조건을 잠시 제거하고 대상 형식의 호환성을 확인하세요.
변환 후 확인: 구문 검사 후 실제 동작 검증하기
1단계: 대상 코어로 설정 검사하기
JSON 문법이 맞아도 sing-box 설정으로 유효하다는 뜻은 아니며, YAML 파싱에 성공해도 코어가 해당 필드를 지원한다는 보장은 없습니다. 해당 명령줄 프로그램이 설치된 환경에서 아래 검사를 실행할 수 있습니다. 실행 파일 이름과 경로는 실제 설치 상태에 맞춰 사용하세요.
mihomo -t -f converted.yaml
sing-box check -c converted.json
알 수 없는 필드, 아웃바운드 누락, 존재하지 않는 정책 그룹 참조 등의 오류를 먼저 해결한 뒤 프록시를 실행하세요. GUI 클라이언트가 병합이나 덮어쓰기를 수행한다면 최종 실행 설정도 확인해야 합니다. 가져온 파일이 검사에 통과해도 덮어쓴 뒤까지 유효하다는 보장은 없습니다.
2단계: 누락되기 쉬운 연결 매개변수 확인하기
- 인증 및 포트: 서버 도메인, 포트, 비밀번호 또는 UUID를 확인하세요. 로컬
7890을 서버 포트로 잘못 입력하지 않도록 주의하세요. - TLS: TLS 사용 여부, 서버 이름, 인증서 검증을 확인하세요. SNI나 서버 이름 매핑 오류를 가리기 위해 인증서 검증을 끄지 마세요.
- 전송 계층: WebSocket 경로, Host 헤더, gRPC 서비스 이름 등을 각각 확인하세요. 프로토콜 이름만 남겨서는 충분하지 않습니다.
- Reality 및 지문: 공개 키, Short ID, 서버 이름, 클라이언트 지문이 대상 구현에서 지원되며 올바르게 설정됐는지 확인하세요.
- UDP: 프로토콜, 노드 서버, 대상 설정이 필요한 UDP 동작을 모두 지원하는지 확인하세요. 웹페이지에 한 번 접속됐다고 UDP까지 사용할 수 있는 것은 아닙니다.
3단계: 분기 규칙과 DNS 확인하기
Clash의 rules는 순서대로 적용되며 마지막의 MATCH가 흔히 기본 규칙 역할을 합니다. sing-box의 라우팅 규칙, 동작, route.final은 별도의 구조를 사용하므로 한 줄씩 그대로 옮길 수 없습니다. 규칙이 지정하는 대상이 실제로 존재하는지, 프록시 서버 도메인 조회가 아직 연결되지 않은 프록시에 의존하지는 않는지 확인해 DNS 조회와 연결이 서로 대기하는 상황을 방지하세요.
DNS의 fake-ip, 도메인 조회 정책, 규칙 집합 형식, 다운로드 경로도 기계적으로 옮겨서는 안 됩니다. Clash의 rule-providers와 sing-box 규칙 집합은 같은 파일 형식이 아닙니다. 텍스트 목록을 읽을 수 있다고 해서 바이너리 규칙 집합까지 바로 공유할 수 있는 것은 아닙니다. TUN은 인터페이스, 라우팅, 시스템 권한도 필요하므로 노드 링크만으로 온전히 표현할 수 없습니다.
자주 묻는 질문: 가져오기, 업데이트, 형식 선택
변환 후 노드는 보이는데 연결되지 않는 이유는 무엇인가요?
먼저 예시용 도메인이나 만료된 구독, 포트 충돌이 원인이 아닌지 확인한 다음 로그에서 오류가 발생한 계층을 살펴보세요. 도메인 조회 실패라면 DNS를, TCP 연결 실패라면 주소·포트·로컬 네트워크를 확인하세요. TLS 핸드셰이크 실패라면 시스템 시간, 서버 이름, 인증서를 점검해야 합니다. Base64와 YAML을 계속 바꾸는 것만으로는 대개 이런 연결 계층 문제를 해결할 수 없습니다.
구독을 업데이트한 뒤 로컬 수정 사항이 사라지는 이유는 무엇인가요?
원격 설정을 업데이트하면 보통 다운로드한 콘텐츠가 교체됩니다. DNS, 규칙, 정책 그룹처럼 계속 유지할 설정은 클라이언트가 지원하는 별도의 오버라이드 또는 병합 기능으로 관리하고, 구독 업데이트 후에도 적용되는지 확인하세요. 변환으로 만든 정적 파일도 자동으로 갱신되지 않습니다. 계속 동기화하려면 다시 가져올 수 있는 원본 경로와 반복 실행 가능한 변환 절차를 유지해야 합니다.
어떤 형식을 선택해야 하나요?
- mihomo만 사용하는 경우: 제공자가 mihomo 호환이라고 명시한 YAML을 우선 선택하고, 전체 설정인지 노드 제공자 파일인지 확인하세요.
- sing-box만 사용하는 경우: 현재 코어 버전에 맞는 JSON을 우선 선택하고 다른 전체 설정 형식을 거쳐 여러 번 변환하지 마세요.
- 여러 코어에서 같은 노드를 사용하는 경우: 신뢰할 수 있는 동일한 노드 소스를 유지하고, 기본 설정 하나를 억지로 공유하기보다 코어별 규칙과 DNS 템플릿을 따로 관리하세요.
- Base64 링크 목록만 있는 경우: 신뢰할 수 있는 환경에서 디코딩하고 변환하세요. 입력·출력 노드 수와 건너뛴 프로토콜을 기록한 뒤 설정을 불러와 테스트하세요.
신뢰할 수 있는 변환 결과는 노드 매개변수가 빠짐없이 유지되고, 대상 설정이 코어 검사를 통과하며, 실제 트래픽이 예상한 규칙대로 처리되는 세 가지 조건을 모두 충족해야 합니다. 원본 구독 경로와 이전에 정상 작동한 설정을 보관해 두면 템플릿이나 코어를 업그레이드한 뒤 달라진 점을 더 빠르게 찾을 수 있습니다. 필드 의미는 YAML 핸드북에서, 기본 개념은 개념 빠른 참조에서 확인할 수 있습니다.