구독 URL과 구독 형식
구독 URL은 설정을 가져오는 주소이고, 형식은 서버가 반환하는 데이터의 구조입니다. HTTPS로 시작하는 주소는 Clash YAML, Base64로 인코딩된 노드 목록, 로그인 페이지 중 무엇이든 반환할 수 있습니다. 링크의 확장자만으로 형식을 판단할 수는 없습니다. .yaml로 끝나지 않는 주소에서도 완전한 설정을 받을 수 있습니다.
문제를 확인할 때는 HTTP 요청 성공 여부, 응답 텍스트 형식, 현재 코어의 프로토콜 지원 여부를 차례로 구분하세요. 구독을 YAML로 변환하면 설정 구조를 맞출 수는 있지만, 오래된 코어에 새 프로토콜 지원을 추가하거나 만료된 노드 인증 정보를 복구할 수는 없습니다.
자주 쓰이는 세 가지 형식의 구조 비교
| 형식 | 판별 단서 | 일반적으로 포함되는 내용 | 이 정보만으로는 알 수 없는 것 |
|---|---|---|---|
| URI 노드 링크 | ss://、trojan://、vmess:// |
단일 노드의 주소, 인증 정보, 일부 전송 매개변수 | 분기 규칙, DNS 설정, 프록시 그룹이 포함됐다는 보장은 없음 |
| Base64 노드 목록 | 디코딩하면 한 줄씩 URI가 표시됨 | 여러 노드 링크를 인코딩한 텍스트 | 디코딩 결과가 Clash YAML이라는 보장은 없음 |
| Clash YAML | proxies:, proxy-groups:, rules: 등의 키 |
노드, 프록시 그룹, 규칙 및 선택 사항인 DNS·리스너 설정 | 모든 Clash 계열 코어가 같은 필드를 지원한다는 보장은 없음 |
URI: 단일 노드 공유 형식
URI는 보통 프로토콜, 인증 정보, 서버, 포트, 쿼리 매개변수, 이름 순으로 구성됩니다. 프로토콜마다 필드 규칙이 다르므로 동일한 방식으로 처리할 수 없습니다. 예를 들어 흔히 쓰이는 vmess:// 공유 문자열은 JSON을 Base64로 감쌉니다. Shadowsocks 공유 형식은 인증 정보 부분만 인코딩할 수도 있습니다. 모든 프로토콜에 같은 문자열 분리 함수를 적용하지 마세요.
다음은 필드를 설명하기 위한 가상의 Trojan 링크입니다. example.com은 문서용 예시 도메인이며, 비밀번호도 설명용 문자열입니다. 실제 프록시 서비스로 사용할 수 없습니다. 쿼리 매개변수의 sni는 TLS 서버 이름이고, 해시(#) 뒤의 내용은 노드 이름입니다.
trojan://[email protected]:443?security=tls&type=tcp&sni=example.com#Lab-Trojan
Base64: 목록을 감싸는 인코딩 계층
일반적인 구독은 여러 URI를 줄바꿈으로 구분한 뒤 전체 텍스트를 한 번 Base64로 인코딩합니다. 올바른 순서는 바깥쪽 인코딩을 먼저 풀고, 각 줄을 해당 프로토콜 파서에 전달하는 것입니다. 디코딩 결과의 특정 줄에 Base64가 남아 있다면 프로토콜 자체의 공유 형식일 수 있으므로, 전체 텍스트를 무작정 반복 디코딩하지 마세요.
일부 제공자는 URL 안전 변형을 사용해 +와 /를 각각 -와 _로 바꾸고, 끝의 =를 생략하기도 합니다. 이는 인코딩 처리 방식의 차이일 뿐, 별도의 프록시 프로토콜이 아닙니다.
YAML: 완전한 설정과 노드 제공자 파일 구분하기
완전한 설정에는 proxies, proxy-groups, rules, dns 등의 설정 섹션이 들어갈 수 있습니다. proxies:만 포함된 파일은 노드 제공자 파일로 쓰이는 경우가 많습니다. 프록시 그룹, 규칙, 리스너 설정까지 갖춘 클라이언트 설정과는 다릅니다. 이런 파일을 완전한 설정으로 가져오면 노드는 표시되지만 규칙이 여전히 모든 연결을 직접 연결할 수 있습니다.
클라이언트에서 바로 가져올 수 있는가
Clash 계열 클라이언트는 대체로 YAML 설정을 사용하지만, GUI에서 무엇을 가져올 수 있는지는 클라이언트 구현에 따라 다릅니다. Clash Verge Rev, FlClash 등의 설정 가져오기 기능과 mihomo 코어의 설정 파일 읽기는 서로 다른 계층입니다. URI 스캔, 노드 붙여넣기, 구독 변환을 지원하는 UI라고 해서 코어가 임의의 Base64 텍스트를 설정 파일로 읽을 수 있는 것은 아닙니다.
- 제공자가 Clash 또는 Mihomo 형식을 지원하는 경우: 대상 설정 형식을 선택한 뒤 구독 URL을 가져오세요. VLESS, Reality 또는 기타 확장 필드가 필요하다면 대상 코어의 지원 여부를 확인해야 합니다.
- URI 하나만 있는 경우: 클라이언트에서 단일 노드 가져오기를 지원하는지 확인하세요. 지원하지 않으면
proxies항목으로 변환하고 프록시 그룹과 규칙을 추가해야 합니다. - Base64 목록만 있는 경우: 먼저 디코딩된 노드의 프로토콜을 확인한 다음, 클라이언트 내장 변환 기능을 사용할지 로컬에서 변환할지 결정하세요. 파일 확장자만 바꿔서는 변환되지 않습니다.
- 완전한 YAML이 이미 있는 경우: URI로 내보냈다가 다시 YAML로 변환하지 말고 바로 가져오세요. 중간 형식에는 원래 설정의 규칙과 DNS 설정이 보존되지 않는 경우가 많습니다.
‘Clash 형식’을 변하지 않는 호환성 보장으로 받아들이면 안 됩니다. 기존 Clash, Clash Premium, Clash Meta(현재는 보통 mihomo라고 부름)는 지원 기능이 서로 다릅니다. unsupported proxy type 또는 알 수 없는 필드 오류가 발생하면 클라이언트 버전, 실제 코어 버전, 프로토콜 종류를 기록한 뒤 호환 범위를 확인하세요. UI 프로그램만 업데이트해도 사용 중인 코어가 바뀌지 않을 수 있습니다.
로컬 변환: 먼저 디코딩한 뒤 노드 필드 매핑하기
1단계: 구독 본문을 제대로 받았는지 확인
응답을 로컬 파일로 저장하고 텍스트 편집기로 첫 부분을 확인하세요. 내용이 <!DOCTYPE html>, 로그인 안내 또는 JSON 오류 객체라면 먼저 요청 실패 문제를 해결해야 합니다. HTTP 200은 서버가 응답했다는 뜻일 뿐, 유효한 구독이라는 뜻은 아닙니다. 401, 403은 인증 또는 접근 제한과 관련된 경우가 많습니다.
일부 서비스는 요청 매개변수나 User-Agent에 따라 서로 다른 형식으로 응답합니다. 브라우저에서는 노드 목록을 받지만 클라이언트에서는 YAML을 받는다면 서버가 형식 협상을 수행하는 것일 수 있습니다. 서비스 제공자의 문서를 확인하고, 인터넷에서 찾은 변환 매개변수를 무작정 추가하지 마세요.
2단계: 로컬에서 목록 디코딩
아래 Node.js 스크립트는 내장 모듈만 사용하며 Base64 디코딩만 수행합니다. 프로토콜 변환 기능은 없습니다. 인코딩된 본문을 subscription.txt로 저장하고 스크립트는 decode-subscription.cjs로 저장하세요. 입력과 출력은 로컬에 보관되지만, 출력 파일에도 민감한 인증 정보가 들어 있습니다.
const fs = require('node:fs');
const { TextDecoder } = require('node:util');
const input = fs.readFileSync('subscription.txt', 'utf8').trim();
const compact = input.replace(/\s+/g, '');
if (!compact || !/^[A-Za-z0-9+/_-]+={0,2}$/.test(compact)) {
throw new Error('입력 내용이 예상한 Base64 목록 형식이 아닙니다. 응답 본문을 먼저 확인하세요');
}
const normalized = compact.replace(/-/g, '+').replace(/_/g, '/');
const bare = normalized.replace(/=+$/, '');
if (bare.length % 4 === 1) {
throw new Error('Base64 길이가 올바르지 않습니다. 입력이 불완전할 수 있습니다');
}
const bytes = Buffer.from(bare, 'base64');
if (bytes.toString('base64').replace(/=+$/, '') !== bare) {
throw new Error('Base64 내용이 잘못되었거나 끝부분에 비정상적인 문자가 있습니다');
}
const text = new TextDecoder('utf-8', { fatal: true }).decode(bytes);
fs.writeFileSync('nodes.txt', text, { mode: 0o600 });
console.log('nodes.txt에 저장했습니다. 로컬에서 프로토콜과 줄 수를 확인하세요');
node decode-subscription.cjs
원문이 이미 URI 목록이나 YAML이라면 이 단계를 실행하지 마세요. 파일 권한 매개변수의 동작은 운영체제마다 다릅니다. 출력 파일은 개인 폴더에 저장하고 자동 동기화되거나 공개 공유되는 폴더는 피하세요. 디코딩 후에는 노드마다 한 줄씩 있는지, 트래픽 안내 등 노드가 아닌 텍스트가 섞이지 않았는지 확인하세요.
3단계: URI 필드를 YAML로 매핑
앞서 본 Trojan 예시를 바탕으로 다음과 같은 최소 예시 설정을 만들 수 있습니다. 로컬 혼합 프록시 포트는 7890, 모드는 rule로 설정하고, 앞선 규칙에 일치하지 않은 연결은 모두 출구 선택으로 보냅니다. 이 설정은 구조를 설명하기 위한 것으로, 예시 서버로 연결 여부를 확인할 수는 없습니다.
mixed-port: 7890
allow-lan: false
mode: rule
log-level: info
proxies:
- name: "Lab-Trojan"
type: trojan
server: example.com
port: 443
password: "training-only"
sni: example.com
skip-cert-verify: false
proxy-groups:
- name: "출구 선택"
type: select
proxies:
- "Lab-Trojan"
- DIRECT
rules:
- MATCH,출구 선택
server는 연결 주소, port는 정수형 포트, password는 URI 인증 정보, sni는 TLS 이름에 해당합니다. 예시의 type=tcp를 YAML의 type: tcp로 기계적으로 복사하지 않은 이유는 여기서 type이 프록시 프로토콜을 뜻하기 때문입니다. 값은 trojan이어야 합니다. 필드 이름만 바꿔서는 변환할 수 없는 이유가 여기에 있습니다.
실제 링크에서 WebSocket, gRPC 또는 Reality를 사용한다면 대상 코어의 문법에 맞춰 경로, Host, 서비스 이름, 공개 키, 짧은 ID 등의 매개변수도 매핑해야 합니다. 서버 주소와 비밀번호만 남기면 문법상 유효해 보여도 연결되지 않는 노드가 만들어질 수 있습니다. 필드의 의미를 모른다면 오류 필드를 삭제하지 말고 해당 프로토콜과 전송 방식을 명시적으로 지원하는 로컬 변환 도구를 선택하세요.
규칙, 프록시 그룹, 노드 필드를 보존하는 방법
| 원본 필드 또는 설정 섹션 | 변환 시 확인할 항목 | 누락 시 흔히 발생하는 문제 |
|---|---|---|
| 노드 이름 | 이름이 고유하고 그룹 내 참조와 정확히 일치하는지 확인 | 프록시 그룹에서 노드를 찾지 못하거나 참조가 모호해짐 |
| TLS 및 전송 매개변수 | SNI, 인증서 검증, 경로 및 프로토콜 확장 필드 | 핸드셰이크 실패 또는 연결 종료 |
proxy-groups |
그룹 이름, 그룹 유형, 구성원 및 테스트 설정 | 출구를 선택할 수 없거나 자동 선택 동작이 달라짐 |
rules |
원래 규칙 순서, 대상 이름, 마지막 기본 규칙 | 프록시를 거쳐야 할 연결이 직접 연결됨 |
dns、tun |
로컬 설정이나 클라이언트 오버라이드로 계속 관리 | 이름 확인 또는 트래픽 가로채기 방식이 달라짐 |
URI 목록에서 YAML을 만들 때 분기 규칙과 프록시 그룹은 대개 변환 템플릿이 새로 생성합니다. 노드에서 복원되는 것이 아닙니다. 템플릿이 DOMAIN-SUFFIX,example.org,DIRECT를 생성했다면 이는 템플릿 규칙일 뿐, 원래 노드 링크에 포함된 규칙이 아닙니다. 마이그레이션 전후에는 노드 매개변수와 라우팅 정책을 각각 비교하세요. 노드 수만 비교해서는 안 됩니다.
완전한 YAML을 URI로 내보내면 많은 설정 맥락이 사라집니다. 프록시 그룹의 중첩 구조, rule-providers, DNS 정책, TUN 설정은 단일 노드 URI로 표현하는 공통 규격이 없습니다. 다시 변환해도 템플릿을 기준으로 새 설정을 생성할 수 있을 뿐, 원본 파일의 주석, 정렬 순서, 모든 기능을 복원할 수는 없습니다.
노드 제공자로 반복 변환 줄이기
서비스 제공자가 노드 형식의 YAML을 지원한다면 노드 업데이트와 로컬 규칙을 분리할 수 있습니다. 아래는 mihomo 설정에서 로컬 proxy-providers를 사용하는 예입니다. 먼저 외부 절차로 ./providers/lab.yaml을 업데이트한 다음, 프록시 그룹에서 use로 참조합니다. 이 파일은 지원되는 제공자 형식이어야 하며, 아직 Base64로 인코딩된 목록이어서는 안 됩니다.
proxy-providers:
lab:
type: file
path: ./providers/lab.yaml
proxy-groups:
- name: "출구 선택"
type: select
use:
- lab
rules:
- MATCH,출구 선택
제공자 파일은 아래와 같은 노드 래퍼 구조를 사용하며, 내부 항목의 필드는 proxies의 노드와 동일합니다. 상대 경로는 코어의 작업 디렉터리나 클라이언트의 관리 방식에 따라 해석되므로, 직접 연 YAML 파일이 있는 폴더를 기준으로 하지 않을 수 있습니다. 파일을 찾지 못하면 코어 로그에서 실제 경로를 확인하세요.
proxies:
- name: "Lab-Trojan"
type: trojan
server: example.com
port: 443
password: "training-only"
sni: example.com
skip-cert-verify: false
HTTP 제공자를 사용하더라도 업데이트 URL은 대상 코어가 받아들일 수 있는 노드 데이터를 반환해야 합니다. 업데이트 간격은 형식 변환 기능이 아닙니다. 완전한 설정에 원격 규칙이나 자동 업데이트 URL이 있다면 함께 검토하세요. 구독이 덮어쓰지 않아야 하는 로컬 설정은 클라이언트의 오버라이드 또는 병합 기능으로 관리할 수 있습니다. 구체적인 메뉴 위치는 클라이언트마다 다릅니다.
가져오기 실패 및 변환 후 확인 목록
먼저 오류가 발생한 계층 파악하기
- YAML 파싱 실패 오류가 표시되는 경우: 들여쓰기에 공백을 사용했는지, 콜론 뒤에 공백이 있는지, 비밀번호와 노드 이름을 올바르게 따옴표로 감쌌는지 확인하세요. 따옴표로 감싸지 않은 텍스트에
#이 있으면 주석으로 처리될 수 있습니다. - 정책을 찾을 수 없다는 오류가 표시되는 경우: 규칙 대상과 프록시 그룹 이름이 일치하는지 확인하세요.
MATCH,출구 선택은 이름이 같은 그룹을 가리켜야 합니다. 노드 이름을 바꿨다면 참조도 함께 수정하세요. - 지원하지 않는 프로토콜이라는 오류가 표시되는 경우: 실제로 로드된 코어를 확인하세요.
type을 다른 프로토콜 이름으로 바꿔도 프로토콜이 변환되지는 않습니다. - 설정은 로드되지만 연결 시간이 초과되는 경우: 노드 인증 정보, TLS, 전송 매개변수를 확인한 다음 네트워크와 서버 상태를 점검하세요. 문법이 유효하다고 노드에 연결할 수 있는 것은 아닙니다.
- 재시작 후 수정 내용이 사라지는 경우: 구독 업데이트로 파일이 다시 생성됐을 가능성이 큽니다. 장기적으로 유지할 수정 사항은 클라이언트 오버라이드 설정에 넣거나 별도의 로컬 설정으로 관리하세요.
순서대로 다시 테스트하기
- 원본 설정을 백업하고 기존 노드 수, 그룹 이름, 규칙 모드를 기록하세요. 유일하게 작동하는 설정 파일을 바로 덮어쓰지 마세요.
- 변환 결과에 인증 정보와 전송 매개변수가 포함됐는지 확인하세요. 민감한 필드를 비교할 때는 로컬에서 작업하고 전체 화면 캡처를 공개 토론 공간에 올리지 마세요.
- mihomo 명령줄 환경이 있다면
mihomo -t -f ./config.yaml을 실행해 설정을 확인할 수 있습니다. 테스트 중에도 외부 파일을 읽거나 원격 리소스에 요청할 수 있으므로 먼저 설정 내용을 검토하세요. - 설정을 불러온 뒤 노드를 직접 하나 선택하고, 규칙 모드가 활성화되어 있는지 확인하세요. 연결 로그에서 실제로 어떤 정책이 적용됐는지도 살펴보세요.
- 예시 포트
7890을 사용한다면curl --proxy http://127.0.0.1:7890 https://example.com/으로 로컬 프록시 연결 지점을 확인할 수 있습니다. 이 테스트에는 실제로 사용할 수 있는 노드 설정이 필요하며, TUN, UDP 또는 모든 규칙의 작동까지 검증하는 것은 아닙니다. - 마지막으로 구독을 한 번 업데이트하고 클라이언트를 다시 시작해 노드가 그대로 있는지, 오버라이드 설정이 유지되는지, 규칙 대상이 존재하지 않는 이름으로 바뀌지 않았는지 확인하세요.
변환 방법은 서비스 제공자가 대상 YAML을 직접 제공하는 방식을 우선하고, 다음으로 클라이언트가 명시적으로 지원하는 로컬 가져오기, 마지막으로 검토 가능한 로컬 변환을 고려하세요. 장기간 업데이트해야 한다면 노드 출처와 로컬 규칙을 분리해 관리하는 편이 설정 전체를 URI로 변환했다가 다시 되돌리는 것보다 문제를 찾기 쉽습니다. 필드 문법은 설정 가이드에서, 클라이언트와 코어 선택은 클라이언트 비교에서 확인하세요.