구독 링크의 실체는 무엇인가
구독 링크는 본질적으로 하나의 HTTP/HTTPS 주소일 뿐이며, 클라이언트가 주기적으로 이 주소에 요청을 보내 텍스트 내용을 받아온 뒤, 어떤 약속된 형식에 따라 텍스트를 노드 목록과 규칙으로 해석합니다. 문제는 이 "약속된 형식"이 하나만 존재하지 않는다는 점입니다. 서로 다른 프록시 생태계가 각자 발전하는 과정에서 나름의 텍스트 구조를 정의했기 때문에, 같은 "구독"이라는 개념 아래 실제로 유통되는 형식은 적어도 세 가지입니다: Clash 계열의 YAML 설정, v2ray/Shadowsocks 계열의 Base64 노드 목록, 그리고 일부 패널 소프트웨어가 출력하는 전용 JSON 형식입니다. 클라이언트가 해당 링크를 "인식"할 수 있는지는 그 형식에 대응하는 파서를 구현했는지에 달려 있습니다.
같은 구독 링크가 Clash 클라이언트에서는 정상적으로 가져와지지만, 다른 프로토콜 스택 기반 클라이언트로 바꾸면 형식 오류나 빈 목록으로 표시되는 이유도 여기에 있습니다. 링크가 손상된 것이 아니라 양쪽이 서로 다른 "언어"를 쓰고 있는 것입니다.
Clash YAML 설정: 구조화되어 있고 가독성과 표현력이 가장 뛰어남
Clash와 Clash Meta(mihomo 코어)가 사용하는 구독 형식은 표준 YAML 텍스트이며, 완전한 설정 파일에는 보통 다음과 같은 최상위 필드가 포함됩니다:
port: 7890
socks-port: 7891
mode: rule
proxies:
- name: "HK-01"
type: ss
server: example.com
port: 443
cipher: aes-256-gcm
password: "your-password"
proxy-groups:
- name: "자동 선택"
type: url-test
proxies: [HK-01]
url: "http://www.gstatic.com/generate_204"
interval: 300
rules:
- DOMAIN-SUFFIX,google.com,자동 선택
- MATCH,DIRECT
보시다시피 YAML 형식은 노드(proxies)뿐만 아니라 노드를 어떻게 그룹화할지(proxy-groups), 트래픽을 어떤 규칙(rules)으로 어느 그룹에 분배할지도 함께 기술합니다. 다른 형식과 구별되는 핵심이 바로 여기 있습니다: YAML 구독은 단순한 주소 목록이 아니라 "실행 가능한 완전한 정책 세트"입니다. 정보량이 많은 만큼 필드 표기와 들여쓰기 단계에도 엄격하며, 공백 하나가 빠지거나 들여쓰기에 탭이 섞여 들어가면 전체 설정 해석이 실패할 수 있습니다.
주의: YAML은 들여쓰기에 매우 민감합니다. 같은 단계에 있는 필드 앞의 공백 수는 완전히 동일해야 하며 탭 키로 들여쓰기해서는 안 됩니다. 설정 파일을 직접 편집할 때는 YAML 문법 강조를 지원하는 편집기를 사용하는 것이 좋습니다. 공백 하나가 빠진 것은 눈으로 확인하기 매우 어렵습니다.
Base64 노드 목록: 가볍지만 "주소 목록"에 불과함
또 다른 흔한 형식은 Shadowsocks, V2Ray, Trojan, VLESS 등 각 프로토콜의 클라이언트 생태계에서 유래합니다. 이런 구독을 열어보면 줄바꿈 없이 이어진, 겉보기에 규칙 없는 문자열인 경우가 많으며 예를 들어 c3M6Ly8...로 시작합니다. 이는 실제로 여러 개의 노드 링크(각각 ss://…, vmess://…, trojan://… 형태)를 줄바꿈으로 이어붙인 뒤 전체를 한 번 더 Base64로 인코딩한 것으로, QR 코드나 순수 텍스트 환경에서 전달하기 편하게 하고 특수 문자가 이스케이프로 손상되는 것을 막기 위한 목적입니다.
디코딩하면 각 줄은 대략 다음과 같은 구조입니다(Shadowsocks 예시):
ss://[email protected]:443#HK-01
vmess://eyJ2IjoiMiIsInBzIjoiSEstMDIiLCJhZGQiOiJleGFtcGxlLmNvbSJ9
trojan://[email protected]:443?sni=example.com#HK-03
이 형식의 정보 밀도는 YAML보다 훨씬 낮습니다. "어떤 노드가 있고, 주소가 무엇이며, 어떤 암호화를 쓰는지"만 기술할 뿐 그룹화 정책이나 분산 규칙은 담고 있지 않습니다. 클라이언트는 이 목록을 받은 뒤 규칙을 어떻게 정할지, 노드를 어떻게 그룹화할지를 자체 내장된 기본 정책으로 결정합니다. 이 때문에 흔한 궁금증이 생깁니다: 같은 Base64 구독을 두 클라이언트에 가져와도 노드 수는 같지만, 프록시 모드의 그룹 구조와 기본 동작은 완전히 다른 경우가 있는데, 이는 그룹화 규칙이 구독 내용의 일부가 아니라 클라이언트가 자체적으로 채워 넣은 것이기 때문입니다.
가져온 뒤 노드가 없거나 바로 오류가 나는 이유
위 두 형식의 차이를 종합해 보면 구독 가져오기 실패는 대체로 다음 몇 가지 원인으로 정리됩니다:
- 형식 불일치. 순수 Base64 노드 목록을 Clash YAML인 것처럼 그대로 넣으면 클라이언트는 YAML 파서로 알아볼 수 없는 문자열을 읽게 되어 당연히 형식 오류가 발생합니다. 반대로 YAML 설정 파일 내용을 Base64로 디코딩하려 해도 의미 없는 문자열만 얻게 됩니다.
- 지원하지 않는 프로토콜 필드. YAML의
type필드에 적힌 프로토콜 유형(예: 일부 최신 전송 계층 확장 파라미터)을 클라이언트 코어 버전이 오래되어 아직 구현하지 못했다면, 해당 노드가 건너뛰어지거나 전체 설정 해석이 실패할 수 있으며 오류 메시지에 보통 어느 줄, 어느 필드인지가 표시됩니다. - User-Agent 차단. 일부 구독 서버는 요청의 User-Agent에 따라 다른 내용을 반환하며, "비공식 클라이언트로 의심되는" 요청에 빈 내용이나 오류 페이지를 돌려주기도 합니다. 이 경우 가져온 뒤 노드 수가 0으로 표시됩니다.
- 인코딩 또는 줄바꿈 문제. Base64 내용을 직접 복사해서 붙여넣을 때 실수로 추가 줄바꿈이나 공백이 섞이거나 내용이 온전히 복사되지 않으면, 디코딩 결과가 잘려서 일부 노드가 빠지거나 전체 해석이 실패할 수 있습니다.
- 링크 자체 만료. 구독 서비스 측 주소 자체가 실효되거나 트래픽 소진으로 반환 내용이 제한되는 경우, 이런 문제는 클라이언트가 스스로 해결할 수 없으며 구독 제공자에게 문의해 확인해야 합니다.
점검 순서를 제안하면: 먼저 구독 링크를 브라우저에서 직접 열어 내용이 정상적으로 표시되는지 확인하고, 반환된 내용의 시작 부분 특징을 확인합니다(YAML은 보통 port:나 proxies:로 시작하고, Base64 목록은 공백 없는 하나의 긴 문자열입니다). 마지막으로 클라이언트 자체의 로그 페이지를 확인하면 대부분의 해석 오류에서 구체적인 오류 줄 번호를 알려줍니다.
형식 간 상호 변환 방법
손에 있는 구독 형식이 클라이언트가 요구하는 형식과 다르다면, 일반적으로는 변환 도구를 이용해 중간에서 한 번 "번역"하는 방식을 쓰며 직접 수정하지는 않습니다. 현재 커뮤니티에서 널리 쓰이는 것은 subconverter 계열의 오픈소스 변환 서비스로, 원리는 원본 구독 링크를 입력하면 목표 형식(Clash YAML, 범용 Base64 목록 등)의 새 링크를 출력해 주는 것입니다. 클라이언트는 이 변환된 새 주소를 그대로 구독하면 되며, 이후 업데이트도 자동으로 반영됩니다.
흔히 쓰이는 두 가지 변환 방향
- Base64 노드 목록에서 Clash YAML로: 변환 서비스가 각
ss:///vmess:///trojan://링크를 해석해 프로토콜 파라미터를proxies필드에 채우고, 목표 클라이언트 유형에 맞춰 기본proxy-groups와rules를 추가로 보충합니다(일부 서비스는 규칙 템플릿을 직접 지정할 수 있습니다). - Clash YAML에서 범용 Base64 목록으로: 변환 서비스는
proxies부분의 노드 정보만 남겨 해당 프로토콜 형식의 링크로 다시 조합하고 Base64로 인코딩합니다. 이 단계에서 그룹화와 규칙 정보는 사라지는데, 목표 형식 자체가 규칙을 기술할 수 없기 때문입니다.
주의: 구조화된 형식을 노드 목록 형식으로 변환하는 것은 한쪽 방향의 정보 손실입니다. 분산 규칙과 그룹화 로직은 보존되지 않습니다. 다른 클라이언트에서 임시로 긴급 사용하는 경우라면 문제가 없지만, 이 변환된 링크에 장기적으로 의존한다면 구독 제공자에게 직접 원본 대응 형식의 주소를 요청하는 것이 좋으며 반복적인 재변환에 오래 의존하지 않는 것을 권장합니다.
상호 변환 시 안전성과 안정성 관련 주의사항
변환 서비스는 본질적으로 중간 프록시이며, 원본 구독 내용이 먼저 변환 서버를 거쳐 클라이언트에 도달합니다. 여기서 몇 가지 유의할 점이 있습니다:
- 가능하면 직접 구축했거나 신뢰할 수 있는 변환 백엔드를 선택합니다. 공용 변환 서버는 자신을 거쳐 간 구독의 원본 내용을 볼 수 있으므로, 구독에 계정 관련 민감한 파라미터가 포함되어 있다면 오픈소스이면서 직접 배포할 수 있는 변환 서비스를 선택하거나, 구독 서비스 제공자가 공식적으로 제공하는 변환 인터페이스만 사용하는 것이 좋습니다.
- 변환 후 노드 수를 대조 확인합니다. 변환 과정에서 일부 인식되지 않는 프로토콜 파라미터가 조용히 버려질 수 있으므로, 변환 전후 노드 수가 일치하는지 비교하고 차이가 크면 변환 로그를 확인하거나 다른 변환 규칙 템플릿으로 바꿔봅니다.
- 규칙 템플릿이 클라이언트 버전과 맞아야 합니다. 서로 다른 규칙 템플릿은 다른 그룹화 습관(예: 스트리밍 그룹을 별도로 구분하는지, 특정 지역 그룹을 내장하는지)에 대응하므로, 변환 시 선택한 템플릿이 자신이 쓰는 클라이언트의 사용 습관과 맞아야 하며 그렇지 않으면 노드 그룹화가 예상과 다르게 나타날 수 있습니다.
- 변환 링크도 갱신 주기에 맞춰 새로고침해야 합니다. 변환 서비스는 보통 원본 구독을 실시간으로 가져와 변환해 반환하므로, 원본 구독이 만료되거나 교체되면 변환 링크도 함께 실효되어 동기화 갱신이 필요합니다.
가져오기 전 3단계 자가 점검
구독 링크가 어떤 형식인지 확실하지 않을 때는 아래 순서로 빠르게 판단하면 반복적으로 가져오기를 시도하다 오류를 겪는 일을 줄일 수 있습니다:
- 브라우저 주소창에서 구독 링크를 직접 열어 반환 내용의 시작 문자를 확인합니다.
port,proxies,mixed-port등의 필드가 나오면 대체로 Clash YAML로 판단할 수 있고, 줄바꿈 없는 하나의 긴 문자열이라면 Base64 노드 목록으로 판단할 수 있습니다. - 현재 사용 중인 클라이언트 코어 유형을 확인합니다(Clash Meta / mihomo 코어는 보통 호환성이 더 넓고 지원 프로토콜 종류가 더 많습니다). 코어 버전이 오래되었다면 먼저 클라이언트를 업데이트한 뒤 가져오는 것이 우선입니다.
- 형식이 정말로 맞지 않는다면 변환 도구를 이용하고, 앞 절의 안전 관련 조언에 따라 변환 백엔드를 선택합니다.
이 세 단계를 거치고 나면 대부분의 "구독 가져오기 실패", "노드 목록이 비어 있음" 문제는 구체적인 원인을 파악할 수 있어 반복적인 시행착오가 필요 없습니다.