구독 주소는 데이터를 가져오는 경로이고, Base64는 인코딩 방식이며, 공유 링크는 개별 연결 정보를 담습니다. 반면 JSON 설정에는 코어 전체의 실행 설정이 들어갈 수 있습니다. 구독 이전, 가져오기 오류 해결, 사용자 지정 설정 보존이 필요한 경우 먼저 입력 형식을 파악하고 필드별로 변환한 뒤 클라이언트에서 연결, 전송, 라우팅 설정을 확인하세요.
1. 주소·인코딩·설정 데이터 구분하기
‘V2Ray 구독 형식’이라는 단일 파일 표준은 없습니다. 일반적인 구독 API는 HTTPS로 텍스트를 반환하며, 이 텍스트는 여러 공유 링크일 수도 있고 링크 목록 전체를 Base64로 인코딩한 결과일 수도 있습니다. 구독 URL 자체가 노드 설정은 아니므로, 클라이언트가 주소에 요청을 보내 응답을 해석해야 합니다.
파일 이름만으로 데이터 형식을 판단할 수는 없습니다. .json으로 끝나는 주소가 오류 페이지를 반환할 수도 있고, 확장자가 없는 주소가 올바른 JSON을 반환할 수도 있습니다. 먼저 응답 내용을 확인한 다음 ‘구독 업데이트’, ‘클립보드에서 가져오기’, ‘사용자 지정 설정 가져오기’ 중 알맞은 방법을 선택하세요.
서비스 제공업체가 지원하는 구독
권장노드를 계속 업데이트해야 한다면 대상 클라이언트를 명시적으로 지원하는 구독을 우선 사용하세요. 일반적으로 공유 링크 목록을 Base64로 인코딩한 형식이지만, 제공업체의 안내와 실제 응답을 기준으로 판단해야 합니다.
적합한 용도: 정기 업데이트, 여러 기기에 각각 가져오기, 노드 주소 변경
공유 링크 한 개 또는 여러 개
vmess://, vless:// 등의 링크를 사용하면 개별 연결을 옮기기 편합니다. 여러 링크는 보통 한 줄에 하나씩 배치합니다. 링크 자체에는 자동 업데이트 기능이 없습니다.
적합한 용도: 임시 테스트, 단일 노드 이전, 연결 필드 확인
JSON 설정
전체 설정에는 인바운드, 아웃바운드, DNS, 라우팅 정보가 포함될 수 있습니다. JSON은 데이터를 담는 문법일 뿐이며, 실제 필드는 대상 코어와 버전에 맞아야 합니다. 구문 분석에 성공했다고 해서 실행 가능한 설정인 것은 아닙니다.
적합한 용도: 고정 라우팅, 사용자 지정 DNS, 실행 환경 전체 이전
2. Base64 구독 식별 및 디코딩
일반적인 링크 목록 구독은 각 공유 링크를 한 줄씩 배치한 뒤, 전체 텍스트를 UTF-8 바이트로 변환해 Base64로 인코딩합니다. 디코딩 결과에는 프로토콜 접두사가 보여야 합니다. 코어 전체 설정이 바로 나오는 것은 아닙니다.
다음 JavaScript 코드는 로컬 문자열 처리만 보여 줍니다. 표준 Base64와 일반적인 URL 안전 문자 집합을 모두 지원합니다. 인코딩된 텍스트의 공백만 제거하며 공유 링크 내부 문자는 건드리지 않습니다. 길이가 맞지 않거나 잘못된 바이트가 있으면 처리를 중단하세요. 반복해서 디코딩해 ‘수정’하려고 하지 마세요.
function decodeSubscription(encoded) {
const text = encoded.trim()
.replace(/\s/g, "")
.replace(/-/g, "+")
.replace(/_/g, "/");
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(text) ||
text.length % 4 === 1) {
throw new Error("Base64 텍스트 형식이 올바르지 않습니다");
}
const padded = text + "=".repeat((4 - text.length % 4) % 4);
const bytes = Uint8Array.from(atob(padded), ch => ch.charCodeAt(0));
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
}
function encodeSubscription(linkText) {
const bytes = new TextEncoder().encode(linkText);
let binary = "";
for (const byte of bytes) binary += String.fromCharCode(byte);
return btoa(binary);
}
- 디코딩 결과에
vmess://또는vless://가 보이면 공유 링크를 한 줄씩 확인하세요. - 원본이
{또는[로 시작하면 먼저 JSON인지 확인하세요. Base64 디코딩부터 시도하지 마세요. - HTML 태그, 로그인 안내 또는 API 오류가 표시되면 구독 권한과 만료 여부, 네트워크 요청 결과를 확인하세요.
- 바깥쪽 데이터를 디코딩한 뒤에도
vmess://링크의 페이로드가 Base64일 수 있습니다. 이는 링크 자체의 인코딩 단계이며, 구독 전체의 디코딩 실패를 뜻하지 않습니다.
3. 공유 링크를 JSON으로 변환: 필드 의미에 맞게 매핑
링크 변환은 접두사만 바꾸는 작업이 아닙니다. 일반적인 VMess 공유 형식은 JSON 데이터를 인코딩해 vmess:// 뒤에 붙입니다. VLESS 공유 링크는 사용자 식별 정보, 호스트, 포트, 쿼리 매개변수, 설명으로 이루어진 URI 형식을 주로 사용합니다. 확장 매개변수 지원 여부는 클라이언트마다 다를 수 있습니다.
vless://[email protected]:443?encryption=none&security=tls&type=tcp&sni=example.com#format-demo
필드 설명을 위한 가상의 연결입니다. example.com은 문서 예시용 도메인이므로 실제 연결에 사용할 수 없습니다. 여기서 encryption=none은 VLESS 사용자 설정을 뜻하며 TLS를 끈다는 의미가 아닙니다. 전송 보안은 security=tls로 지정합니다.
| 링크 정보 | JSON에서 흔히 사용하는 위치 | 확인할 점 |
|---|---|---|
| 호스트와 포트 | settings.vnext[].address、port | 포트는 숫자로 입력하고, 도메인을 설명란에 잘못 입력하지 않았는지 확인하세요 |
| 사용자 식별 정보 | settings.vnext[].users[].id | UUID 전체를 URI 구분자 없이 그대로 유지하세요 |
encryption=none | users[].encryption | VLESS 전용 필드입니다. VMess에 그대로 적용할 수 없습니다 |
security=tls | streamSettings.security | VMess 사용자의 암호화 설정과 혼동하지 마세요 |
sni=example.com | tlsSettings.serverName | 서버에서 요구하는 TLS 이름을 유지하세요 |
type=tcp | streamSettings.network | 전송 이름과 허용되는 별칭은 코어 버전에 맞는지 확인하세요 |
다음은 해당 필드를 지원하는 Xray 코어용 구조 예시입니다. 10808은 루프백 주소에만 바인딩되고 443은 원격 포트이므로 서로 바꿔 쓸 수 없습니다. 여기서는 기존의 tcp 표기를 사용합니다. 전송 이름과 호환 별칭은 코어 버전의 문서를 확인하세요.
{
"inbounds": [
{
"tag": "local-socks",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true
}
}
],
"outbounds": [
{
"tag": "proxy",
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "example.com",
"port": 443,
"users": [
{
"id": "7c9c1f0b-4bd8-4b44-8eb2-3eb2ac702783",
"encryption": "none"
}
]
}
]
},
"streamSettings": {
"network": "tcp",
"security": "tls",
"tlsSettings": {
"serverName": "example.com"
}
}
}
]
}
4. JSON을 공유 링크로 변환: 사라질 수 있는 정보
전체 설정을 공유 링크로 내보낼 때 변환기는 보통 프록시 아웃바운드 하나만 추출합니다. JSON에는 여러 outbounds가 있을 수 있으며, 그중에는 직접 연결이나 차단 아웃바운드도 포함될 수 있습니다. 변환 전에 대상 프록시의 tag를 선택하세요. 배열의 첫 항목이 항상 노드인 것은 아닙니다.
변환 방법: 연결 이전과 전체 설정 이전을 따로 처리
연결만 이전
- 대상 아웃바운드에서 프로토콜, 서버, 포트, 사용자 정보를 추출합니다.
- TLS 이름, 전송 경로, 서버에서 요구하는 흐름 제어 설정을 유지합니다.
- 공유 링크를 만든 뒤 대상 클라이언트에서 로컬 프록시와 라우팅을 다시 설정합니다.
기존 동작 전체 유지
- JSON 설정을 그대로 보존하고 대상 코어에서 읽을 수 있는지 확인합니다.
- 인바운드 포트, DNS, 라우팅 규칙, 외부 데이터 파일을 확인합니다.
- 클라이언트의 사용자 지정 설정 가져오기를 사용하고 설정 내용이 변경되는지 확인합니다.
공유 링크는 연결 매개변수를 전달하는 데 적합합니다. 전체 실행 동작을 유지하려면 설정 파일, 의존 리소스, 코어 기능을 모두 함께 보존해야 합니다.
routing.rules, dns, 로컬 리스닝 주소, 앱별 설정은 일반적인 노드 공유 링크에 포함되지 않는 경우가 많습니다. JSON을 링크로 바꾼 뒤 다시 JSON으로 가져오면 원본과 동일한 설정이 되지 않을 수 있습니다. 연결에 성공하더라도 라우팅 결과가 같다는 뜻은 아닙니다.
- 원본 JSON을 먼저 백업하고 이전할 아웃바운드의
tag를 기록하세요. - 노드 이름만 비교하지 말고 서버 주소, 포트, 사용자 정보, 전송, TLS, 흐름 제어 설정을 하나씩 대조하세요.
- 설명과 쿼리 매개변수는 URI 규칙에 따라 인코딩하세요. 특히 경로의
?,&,#이 링크 구분자로 오인되지 않도록 주의하세요. - 변환기가 지원하지 않는 필드를 알리면 자동 이전을 중단하고 직접 확인하세요. 해당 필드를 조용히 삭제한 채 계속 사용하지 마세요.
5. 공유 링크를 구독으로 다시 묶기: 업데이트 기능은 자동으로 복구되지 않습니다
공유 링크 여러 개를 줄바꿈으로 연결한 뒤 전체 텍스트를 Base64로 인코딩하면 기존 링크 목록 구독 형식의 데이터를 만들 수 있습니다. 하지만 이는 특정 시점의 스냅샷일 뿐입니다. 클라이언트가 정기적으로 가져오게 하려면 직접 관리하거나 신뢰할 수 있는 서비스 제공업체가 운영하는, 계속 접속 가능한 구독 주소가 필요합니다.
- 입력 정리: 한 줄에 완전한 링크 하나만 남기고 빈 줄을 삭제하세요. 웹페이지 제목이나 설명을 함께 인코딩하지 마세요.
- 인코딩: UTF-8을 사용하고 한글 설명이 올바르게 인코딩되는지 확인하세요. 한글 문자열을 단일 바이트 문자만 처리하는 방식으로 다루지 마세요.
- 결과 확인: 인코딩 결과를 다시 디코딩해 링크 개수와 줄 순서, 매개변수가 그대로 유지되는지 확인하세요.
- 업데이트 관리: 노드 주소나 인증 정보가 바뀌어도 정적인 스냅샷은 원래 구독을 따라 변경되지 않습니다. 내용을 다시 생성해 게시해야 합니다.
JSON 설정도 전체를 한 번 Base64로 인코딩한다고 모든 클라이언트에서 쓸 수 있는 구독이 되지는 않습니다. 인코딩은 내부 데이터 구조를 바꾸지 않습니다. 대상 가져오기 기능이 공유 링크 목록만 지원한다면, 디코딩한 전체 JSON을 넣어도 가져오기에 실패할 수 있습니다.
6. v2rayN 및 v2rayNG에서 변환 결과 확인
기존 수동 설정이 구독 업데이트로 덮어쓰이지 않도록 별도의 테스트 그룹에 먼저 가져오세요. 아래는 흔히 볼 수 있는 메뉴 이름입니다. 버전에 따라 순서나 문구가 다를 수 있으니 현재 화면을 기준으로 조작하세요. 전체 JSON을 구독 URL 입력란에 붙여 넣지 마세요.
- v2rayN 구독: ‘구독 그룹’ → ‘구독 그룹 설정’을 열어 테스트 그룹을 추가하고 신뢰할 수 있는 구독 URL을 입력한 다음 해당 그룹의 업데이트 명령을 실행합니다.
- v2rayN 공유 링크: 링크를 복사하고 기본 화면에서 ‘클립보드에서 URL 일괄 가져오기’를 선택한 뒤 새 항목이 추가된 그룹을 확인합니다.
- v2rayNG 공유 링크: 오른쪽 위의 ‘+’ → ‘클립보드에서 가져오기’를 누른 뒤 새 항목을 편집해 서버, 포트, 전송 매개변수를 확인합니다.
- v2rayNG 구독: 메뉴의 ‘구독 그룹 설정’에서 주소를 추가하고 돌아와 ‘구독 업데이트’를 실행합니다. 테스트 그룹을 업데이트했는지 확인하세요.
- JSON 설정: 현재 버전에서 제공하는 사용자 지정 설정 파일 가져오기를 사용하세요. 먼저 로그에서 설정이 올바르게 파싱되었는지 확인한 뒤 연결을 시작합니다.
실행 후 세 단계로 확인하세요. 첫째, 코어가 설정을 읽고 로컬 포트에서 대기하는지 확인합니다. 둘째, 원격 서버와 핸드셰이크가 완료되는지 확인합니다. 셋째, 앱 트래픽이 프록시로 들어와 예상한 라우팅 규칙에 적용되는지 확인합니다. 목록에 노드가 표시되는 것은 데이터가 저장되었다는 뜻일 뿐, 나머지 두 단계까지 확인한 것은 아닙니다.
디코딩하면 링크가 보이는데 가져오기에 실패하는 이유는 무엇인가요?
여러 링크를 한 줄에 붙여 넣었거나 따옴표와 쉼표까지 함께 복사하지 않았는지 확인하세요. 먼저 링크 하나만 가져온 다음 프로토콜과 전송 방식이 대상 코어에서 지원되는지 확인합니다. 단일 링크부터 실패한다면 전체 목록을 다시 인코딩해도 해결되지 않습니다.
가져오기는 성공했는데 실행하자마자 포트 사용 중 오류가 뜨면 어떻게 하나요?
사용자 지정 JSON의 inbounds[].port를 확인하세요. 예시의 10808을 다른 프로세스가 사용 중이라면 충돌하는 프로세스를 종료하거나 포트를 바꾸고 앱의 프록시 주소도 함께 수정합니다. 원격 포트인 443은 변경할 필요가 없습니다.
변환 후 TLS 핸드셰이크가 실패하면 인증서 검증을 꺼야 하나요?
먼저 서버 주소, serverName, 시스템 시간, 전송 설정을 비교하세요. 변환기가 SNI를 누락했거나 TLS 유형을 잘못 바꾸지 않았는지 확인합니다. 인증서 검증을 꺼서 불일치를 감추기보다 해당 필드를 바로잡으세요.
노드는 연결되는데 중국 본토와 해외 트래픽의 라우팅이 달라진 이유는 무엇인가요?
공유 링크에는 기존 routing과 dns가 포함되지 않는 경우가 많습니다. 대상 클라이언트에서 라우팅 설정을 다시 선택하고 규칙 순서와 데이터 파일을 확인하세요. 원래 설정을 재현해야 한다면 전체 JSON을 이전하고 코어 호환성도 점검해야 합니다.
구독을 업데이트했더니 수동으로 수정한 설정이 다시 바뀌었나요?
구독 관리 항목은 업데이트 시 다시 만들어질 수 있습니다. 유지할 설정을 별도의 수동 그룹에 복사한 다음 서비스 제공업체에 원래 구독 매개변수를 확인하세요. 구독에서 생성된 항목을 로컬 수정 사항의 장기 보관 장소로 사용하지 마세요.
마지막으로 원본 입력의 안전한 사본, 변환에 사용한 필드 매핑, 대상 클라이언트에서 확인한 결과를 보관하세요. 이후 문제가 생기면 구독 응답 변경, 변환 중 필드 누락, 로컬 라우팅 또는 포트 설정 변경 중 무엇이 원인인지 구분할 수 있습니다.