먼저 구독 주소, 서버 공유 링크, 전체 설정 파일을 구분한 뒤 변환 가능 여부를 확인하세요. Base64 구독은 보통 여러 공유 링크를 인코딩한 것입니다. 반면 JSON 설정에는 로컬 수신 포트, DNS, 라우팅 등이 포함되므로 확장자만 바꿔 구독으로 만들 수 없습니다. ‘구독을 인식하지 못함’, ‘JSON 가져오기 실패’ 문제를 겪거나 다른 클라이언트로 옮기려는 사용자를 위해 로컬 인코딩 방법, 필드 대응 관계, 가져온 뒤 확인할 항목을 정리했습니다.
먼저 내용부터 확인: 구독 주소와 구독 형식은 다릅니다
흔히 말하는 ‘V2Ray 구독’은 모든 클라이언트에서 통용되는 단일 파일 규격이 아닙니다. https://로 시작하는 구독 주소는 클라이언트가 특정 API에 접속해야 한다는 뜻일 뿐입니다. 실제 형식은 서버가 반환하는 본문에 따라 링크 목록, Base64 문자열 또는 특정 설정 파일이 될 수 있습니다. 같은 API도 요청 매개변수나 클라이언트 식별 정보에 따라 다른 내용을 반환할 수 있으므로 주소 끝부분만 보고 호환성을 판단하면 안 됩니다.
| 형식 | 일반적인 형태 | 실제로 저장된 내용 | 적합한 가져오기 방법 |
|---|---|---|---|
| 링크 목록 구독 | 프로토콜 링크 여러 줄 또는 하나의 긴 Base64 문자열 | 여러 서버의 연결 정보 | 구독 그룹 및 업데이트 기능 |
| 단일 공유 링크 | vmess:// 또는 vless:// | 서버 한 대의 연결 정보와 메모 | 클립보드에서 서버 가져오기 |
| 전체 JSON 설정 | inbounds, outbounds 등의 필드 포함 | 코어의 전체 실행 설정 | 코어와 호환되는 사용자 지정 설정 메뉴 |
구독 본문을 가져오면 먼저 오류 페이지가 아닌지 확인하세요. 시작 부분에 HTML 태그, 로그인 안내, ‘접근 기간이 만료되었습니다’ 같은 문구가 보인다면 예상한 노드 정보를 받은 것이 아닙니다. 오류 페이지를 다시 Base64로 인코딩해도 인코딩된 오류 페이지가 될 뿐, 구독 문제가 해결되지는 않습니다.
용도에 따라 형식 선택: 일상적인 업데이트와 전체 설정은 따로 관리
v2rayN, v2rayNG, v2flyNG에서 서버 목록과 실행 설정은 서로 다른 계층입니다. 클라이언트는 서버 정보를 바탕으로 코어 설정을 생성할 수 있지만, 전체 설정에 포함된 로컬 포트와 트래픽 분기 정책을 공유 링크 하나에 그대로 담을 수는 없습니다. 형식을 선택하기 전에 옮기려는 대상이 ‘노드’인지 ‘노드와 로컬 정책’인지 먼저 정하세요.
클라이언트 호환 링크 목록 구독
추천원격 구독 주소를 저장해 두고 클라이언트에서 서버 목록을 자동 또는 수동으로 가져옵니다. 제공자가 노드를 변경하면 구독을 업데이트해 새 정보를 받을 수 있습니다.
적합한 경우: 여러 서버를 관리하고 노드 목록을 계속 업데이트해야 할 때
단일 VMess / VLESS 공유 링크
서버 한 대를 가져오거나 연결 정보를 항목별로 확인하기 편리합니다. 가져온 링크는 대개 로컬 서버 항목으로 저장되며, 기존 구독의 업데이트 관계까지 자동으로 이어지지는 않습니다.
적합한 경우: 노드 하나를 임시로 테스트하거나 서버 몇 대를 수동으로 옮길 때
코어 호환 전체 JSON
인바운드, 아웃바운드, DNS, 라우팅 등의 설정을 표현할 수 있습니다. 대상 코어가 해당 필드를 지원하는지 확인해야 하며, 파일 확장자만으로 호환성을 판단해서는 안 됩니다.
적합한 경우: 사용자 지정 라우팅, 고정 로컬 수신 설정, 다중 아웃바운드를 관리할 때
v2rayNG는 Xray 코어를, v2flyNG는 V2Fly 코어를 사용합니다. v2rayN의 실제 지원 범위는 선택한 코어에 따라 달라집니다. 모두 JSON이라고 해서 설정을 그대로 공유할 수 있는 것은 아니며, 모두 VLESS라고 해도 모든 전송 방식과 보안 설정을 대상 버전에서 인식하는 것은 아닙니다. 변환하기 전에 대상 클라이언트와 코어의 실제 지원 범위를 확인하세요.
Base64 구독 디코딩하기: 포장을 풀고 각 줄을 확인
일반적인 Base64 구독은 여러 공유 링크를 줄바꿈으로 연결한 뒤 전체 텍스트를 인코딩해 만듭니다. 바깥쪽 인코딩을 풀면 vmess://와 vless:// 링크가 함께 나올 수 있습니다. 특히 기존 VMess 공유 링크 자체에도 Base64 인코딩이 한 겹 더 적용됩니다. 따라서 구독의 바깥쪽 인코딩을 풀었다고 각 VMess 노드까지 디코딩된 것은 아닙니다.
Base64는 암호화가 아닌 인코딩입니다. 표준 Base64에는 +, /와 끝부분의 =가 쓰입니다. URL 안전 변형은 -, _를 사용하며, 출력에 따라 패딩을 생략하기도 합니다. 한글 메모는 UTF-8로 처리해야 합니다. 문자 인코딩을 고려하지 않고 변환하면 글자가 깨질 수 있습니다.
아래 함수는 로컬 브라우저의 빈 페이지에서 개발자 도구를 열어 사용할 수 있습니다. 입력한 텍스트만 처리하며 네트워크 요청은 하지 않습니다. 예시 도메인, UUID, 포트는 구조 설명용이며 실제로 연결할 수 있는 서버 정보가 아닙니다.
function decodeBase64Utf8(input) {
let text = input.trim().replace(/\s+/g, "")
.replace(/-/g, "+").replace(/_/g, "/");
if (text.length % 4 === 1) {
throw new Error("Base64 길이가 올바르지 않습니다");
}
text = text.padEnd(Math.ceil(text.length / 4) * 4, "=");
const bytes = Uint8Array.from(atob(text), ch => ch.charCodeAt(0));
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
}
function encodeBase64Utf8(input) {
const bytes = new TextEncoder().encode(input);
const binary = Array.from(bytes, byte => String.fromCharCode(byte)).join("");
return btoa(binary);
}
const exampleLink = "vless://[email protected]:443?encryption=none&security=tls&type=tcp&sni=example.com#TLS-demo";
const encodedList = encodeBase64Utf8(exampleLink + "\n");
console.log(decodeBase64Utf8(encodedList));
- 먼저 본문에 프로토콜 링크가 일반 텍스트로 이미 표시되어 있는지 확인하세요. 그렇다면 다시 디코딩할 필요 없이 목록을 바로 살펴보면 됩니다.
- 본문이 하나의 긴 인코딩 문자열이라면 디코딩 함수로 바깥쪽 인코딩을 풀고, 줄바꿈을 기준으로 나눈 뒤 빈 줄을 제외하세요.
- 각 줄의 프로토콜 접두사를 확인하세요. 기존 VMess 인코딩 형식임을 명확히 확인한 경우에만 접두사 뒤의 내용을 다시 디코딩하세요.
- 디코딩에 실패하면 원문을 보존하고 내용이 잘렸는지, 웹페이지가 포함됐는지, 문자가 바뀌었는지 확인하세요. 무작정 여러 번 디코딩해도 문제가 해결되지는 않습니다.
VMess와 VLESS 공유 링크: 구조가 다르므로 접두사를 바꿔 쓸 수 없습니다
기존 VMess 링크: JSON을 다시 인코딩
기존 VMess 공유 링크는 일반적으로 vmess:// 뒤에 Base64로 인코딩된 노드 JSON이 붙는 형식입니다. 이 노드 JSON은 코어의 전체 설정이 아니며 로컬 인바운드와 라우팅을 모두 담지 않습니다. 아래는 인코딩 전의 일반적인 필드 구조입니다. 여기서 v는 공유 형식의 버전을 나타내며 클라이언트나 코어 버전이 아닙니다.
{
"v": "2",
"ps": "VMess TLS 예시",
"add": "example.com",
"port": "443",
"id": "11111111-1111-4111-8111-111111111111",
"aid": "0",
"scy": "auto",
"net": "tcp",
"type": "none",
"host": "",
"path": "",
"tls": "tls",
"sni": "example.com"
}
이 객체를 직렬화한 뒤 앞서 소개한 인코딩 함수를 사용하면 기존 형식의 링크를 만들 수 있습니다: "vmess://" + encodeBase64Utf8(JSON.stringify(node)). 여기서 node는 위 객체입니다. 역으로 파싱할 때는 접두사를 제거한 뒤 디코딩하고 JSON.parse로 읽으세요. 여기의 aid: "0"은 일반적인 VMess AEAD 설정에 해당합니다. 오래된 안내를 따라 임의의 다른 값으로 바꾸지 마세요.
VLESS 링크: URI 필드와 쿼리 매개변수
VLESS는 보통 URI 형식을 그대로 사용합니다. 사용자 식별 정보는 @ 앞에, 서버 주소와 포트는 그 뒤에 오며, 전송 및 보안 설정은 쿼리 부분에, 메모는 # 뒤에 표시됩니다. 한글 메모는 퍼센트 인코딩해야 합니다. 필드에 포함된 특수 문자도 URI 도구를 사용해 올바르게 인코딩해야 하며, 처리하지 않은 텍스트를 단순히 이어 붙이면 안 됩니다.
vless://[email protected]:443?encryption=none&security=tls&type=tcp&sni=example.com#TLS-demo
encryption=none은 VLESS 프로토콜 계층의 설정이며 TLS를 끈다는 뜻이 아닙니다. TLS 사용 여부는security=tls등의 설정으로 지정합니다.sni는 TLS 핸드셰이크에 사용할 서버 이름입니다. WebSocket의 HTTP Host나 접속 주소와 혼용하지 마세요.type=tcp는 예시의 전송 유형을 나타냅니다. 다른 전송 방식으로 바꾸려면 해당 전송 방식에 맞는 매개변수와 서버 지원도 필요합니다.- VMess와 VLESS는 인증 방식과 설정 구조가 다릅니다.
vmess://를vless://로 바꿔도 프로토콜이 변환되거나 서버 설정이 바뀌지 않습니다.
JSON 설정 변환하기: 노드만 추출하고 전체 동작은 옮기지 않습니다
전체 설정에는 ‘트래픽을 로컬에서 어떻게 받는지’와 ‘원격으로 어떻게 보내는지’가 모두 들어 있습니다. 아래는 Xray 형식의 간단한 구조 예시입니다. 로컬에서는 루프백 주소의 SOCKS 포트만 수신하고, VLESS TLS 아웃바운드 하나로 예시 도메인에 연결합니다. 필드 계층을 보여 주기 위한 예시일 뿐, 실제로 사용할 수 있는 서버 인증 정보가 포함되어 있지 않으며 모든 코어에서 통용되는 설정도 아닙니다.
{
"log": {
"loglevel": "warning"
},
"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": "11111111-1111-4111-8111-111111111111",
"encryption": "none"
}
]
}
]
},
"streamSettings": {
"network": "tcp",
"security": "tls",
"tlsSettings": {
"serverName": "example.com"
}
}
}
]
}
| JSON 필드 | 공유 링크 위치 | 예시 값 |
|---|---|---|
settings.vnext[0].address | 호스트 이름 | example.com |
settings.vnext[0].port | 원격 포트 | 443 |
settings.vnext[0].users[0].id | URI 사용자 정보 | 예시 UUID |
streamSettings.security | security 쿼리 매개변수 | tls |
tlsSettings.serverName | sni 쿼리 매개변수 | example.com |
inbounds[0].port | 서버 공유 링크에 포함되지 않음 | 10808은 로컬 포트입니다 |
위 표의 아웃바운드 하위 필드는 모두 outbounds[0]을 기준으로 합니다. 직접 추출할 때는 실제 프록시 아웃바운드를 선택한 뒤 해당 아웃바운드의 프로토콜과 전송 매개변수를 확인하세요. 항상 배열의 첫 번째 항목을 선택하면 안 됩니다. 실제 설정에서는 첫 항목이 직접 연결 아웃바운드일 수 있으며, 프록시 아웃바운드가 여러 개이거나 연결 체인 또는 부하 분산이 설정되어 있을 수도 있습니다.
- JSON에서 공유 링크로 변환: 대상 아웃바운드에서 표현할 수 있는 필드를 추출하고 해당 프로토콜의 공유 링크 규격에 맞춰 생성하세요. DNS, 라우팅, 로컬 수신 설정과 다른 아웃바운드는 별도로 보관해야 합니다.
- 공유 링크에서 JSON으로 변환: 먼저 클라이언트에 노드를 가져와 현재 설정에 맞는 코어 실행 설정을 생성하세요. 직접 작성하려면 원격 필드뿐 아니라 로컬 인바운드와 기본 아웃바운드 동작도 지정해야 합니다.
- 링크 목록을 Base64 구독 본문으로 변환: 유효한 공유 링크를 줄바꿈으로 연결한 뒤 UTF-8 Base64로 인코딩하세요. 결과는 구독 본문이지 원격 업데이트가 가능한 구독 주소가 아닙니다.
- Base64를 전체 JSON으로 변환: 먼저 디코딩해 내용을 확인한 뒤 노드별로 변환하세요. 인코딩된 텍스트를
outbounds에 그대로 넣으면 코어 설정 구조에 맞지 않습니다.
클라이언트 가져오기 및 이전: 먼저 백업하고 노드 하나로 확인
수동으로 변환하기 전에 기존 설정을 내보내거나 백업하고 원래 구독 주소도 보관하세요. 사용 중인 구독 그룹을 바로 덮어쓰지 마세요. 테스트용 그룹을 새로 만들어 노드를 몇 개만 가져오면 형식 파싱 오류와 서버 연결 오류를 구분할 수 있습니다. 아래 메뉴 이름은 일반적인 중국어 인터페이스 기준이며 클라이언트 버전에 따라 다를 수 있습니다.
이전 방법: 서버 정보와 로컬 설정을 따로 처리
데스크톱: v2rayN
- 원격 주소는 「구독 그룹」→「구독 그룹 설정」에서 추가한 뒤 대상 그룹을 업데이트하세요.
- 단일 링크는 「서버」→「클립보드에서 URL 여러 개 가져오기」를 선택하고 가져온 결과를 확인하세요.
- 로컬 수신 포트, 선택한 코어, 라우팅 설정을 확인하세요. 원격 서버의 443 포트를 로컬 프록시 포트로 사용하면 안 됩니다.
안드로이드: v2rayNG / v2flyNG
- 원격 구독은 구독 그룹 설정에서 추가하고, 단일 링크는 메인 화면의 클립보드 가져오기 메뉴를 사용하세요.
- v2rayNG는 Xray 지원 범위를 기준으로 매개변수를 확인하고, v2flyNG는 V2Fly 지원 범위를 기준으로 확인하세요.
- VPN 권한과 앱별 설정을 다시 확인하세요. 이러한 로컬 설정은 일반적인 노드 링크로 옮겨지지 않습니다.
같은 구독을 데스크톱과 모바일의 공통 출처로 사용할 수는 있지만, 업데이트 시점과 프로토콜 지원, 로컬 정책은 각 클라이언트에서 따로 확인해야 합니다.
가져온 뒤 노드 편집 화면을 열어 원본과 주소, 포트, 사용자 식별 정보, 전송 유형, TLS 매개변수를 대조하세요. 그런 다음 연결을 시작하고 로그를 확인합니다. 목록은 정상인데 핸드셰이크에 실패한다면 연결 정보와 서버 상태를 확인하세요. 노드가 하나도 표시되지 않는다면 라우팅부터 수정하지 말고 응답 본문과 가져오기 메뉴를 먼저 점검하세요.
- 로컬 연결: 시스템 프록시를 사용하거나 프록시를 수동으로 지정하는 앱은 클라이언트가 실제로 수신 중인 주소와 포트에 연결해야 합니다. 예시의
127.0.0.1:10808이 모든 설치 환경의 기본값은 아닙니다. - 원격 연결: 연결 시간 초과, 인증 실패, TLS 이름 불일치는 서로 다른 문제입니다. 로그를 확인해 각각 대응하고 모든 필드를 한꺼번에 변경하지 마세요.
- 업데이트 관계: 클립보드에서 가져온 고정 노드는 원래 구독 주소가 업데이트되어도 자동으로 동기화되지 않습니다. 자동 업데이트가 필요하다면 구독 주소와 해당 그룹을 저장하세요.
구독 변환 FAQ: 디코딩 성공이 연결 성공을 뜻하지는 않습니다
Base64를 디코딩했는데도 vmess로 시작합니다. 덜 풀린 건가요?
일반적인 바깥쪽 구독 목록이라면 정상입니다. 먼저 줄 단위로 나누세요. 기존 VMess 링크는 vmess:// 뒤의 내용을 디코딩해 노드 JSON을 확인할 수 있습니다. 혼합된 링크 목록 전체를 반복해서 디코딩하지 마세요. URI의 기호까지 인코딩된 데이터로 처리될 수 있습니다.
JSON을 받았는데 ‘클립보드에서 가져오기’에서 서버가 없다고 나오는 이유는 무엇인가요?
먼저 최상위 필드를 확인하세요. inbounds, outbounds가 있다면 전체 코어 설정일 수 있습니다. 대상 버전에서 지원하는 사용자 지정 설정 메뉴를 사용하거나 이 문서의 방법대로 프록시 아웃바운드 하나를 추출하세요. 클립보드 공유 링크 가져오기는 모든 JSON을 지원하는 가져오기 도구가 아닙니다.
Base64로 바꿨는데도 클라이언트에서 가져올 수 없는 이유는 무엇인가요?
먼저 디코딩해 원문이 손상되지 않았는지 확인한 다음 각 줄이 대상 클라이언트에서 지원하는 프로토콜 링크인지 살펴보세요. Base64는 텍스트 표현만 바꿉니다. 빠진 사용자 식별 정보를 채우거나, 웹페이지를 노드로 바꾸거나, 코어에 없는 프로토콜 지원을 추가하지는 않습니다.
변환한 노드는 연결되는데 트래픽 분기 규칙은 왜 따라오지 않나요?
일반적인 공유 링크는 원격 노드 하나의 정보만 표현하며 DNS, 라우팅, 앱별 설정 전체를 옮기지는 않습니다. 대상 클라이언트에서 로컬 정책을 확인하세요. 라우팅 설정 전체를 재현해야 한다면 원본 JSON을 보관하고 코어 필드 호환성을 확인해야 합니다. 노드 연결 성공만으로 설정 이전이 끝난 것은 아닙니다.
확인 순서는 다음과 같습니다. 먼저 올바른 본문을 받았는지 확인하고, 인코딩 단계를 구분한 다음, 노드 필드를 점검하고, 마지막으로 대상 코어와 로컬 트래픽 경로를 확인하세요. 구독, 공유 링크, JSON 설정은 각각 용도가 다릅니다. 각 단계에서 무엇이 유지되고 무엇이 빠지는지 알아야 연결 문제와 설정 문제를 혼동하지 않고 변환할 수 있습니다. 관련 개념은 용어 설명에서, 라우팅과 코어 설정은 고급 설정에서 확인할 수 있습니다.