V2Ray 설정 파일 가이드
연결 처리 순서에 따라 JSON, 인바운드, 아웃바운드, 라우팅, DNS, policy를 살펴봅니다. 처음 설치하거나 구독을 추가하려면 빠른 시작을 먼저 확인하세요. 이 페이지에서는 필드를 확인하고 내보낸 설정을 읽으며 설정 계층별 문제를 찾을 수 있습니다.
1. JSON 구조와 클라이언트의 역할
GUI 설정에서 코어 설정까지
데스크톱에서는 v2rayN을 주로 사용해 서버, 구독 그룹, 시스템 프록시와 코어 실행 옵션을 관리합니다. 실제 연결 처리는 선택한 코어가 담당합니다. Android 클라이언트인 v2rayNG는 Xray 코어를, v2flyNG는 V2Fly 코어를 사용합니다. 세 클라이언트의 설정 화면은 서로 다르며 생성되는 JSON도 완전히 같지 않을 수 있습니다. 설정 파일을 읽기 전에 클라이언트에서 현재 사용하는 코어를 확인하고, 파일이 전체 실행 설정인지 단일 서버 공유 데이터인지 확인하세요. 구독 주소, 공유 링크, 클라이언트 데이터베이스, 코어 설정은 서로 다른 데이터 형식입니다. 모두 서버 정보가 들어 있다고 해서 서로 바꿔 쓸 수 있는 것은 아닙니다.
일반적인 연결은 ‘기기 내 앱에서 인바운드로 진입 → 라우팅에서 아웃바운드 선택 → 아웃바운드에서 원격 서버에 연결’하는 순서로 이해할 수 있습니다. inbounds는 연결을 받는 방식을, outbounds는 연결을 보내는 방식을 정의하고, routing은 연결을 지정된 출구로 보냅니다. dns는 도메인 조회가 필요한 단계의 DNS 설정을, policy는 연결 타이머와 통계 옵션을, log는 기록할 진단 정보를 설정합니다. 모든 설정 파일에 최상위 객체를 전부 명시할 필요는 없습니다. 필드를 생략하면 코어의 기본 동작을 사용한다는 뜻이지, 연결 과정에서 해당 기능이 존재하지 않는다는 뜻은 아닙니다.
실행 가능한 최소 구조부터 살펴보기
아래 전체 설정은 로컬 루프백 주소에 SOCKS 인바운드를 열고 모든 연결을 대상에 직접 연결합니다. 설정 구조와 리스닝 동작을 살펴보기 위한 예제이며 원격 프록시 서버가 없어 암호화 터널을 만들지 않습니다. 실행 전에 포트를 다른 클라이언트가 사용 중인지 확인하세요. 예제에서 라우팅과 출구 태그를 명시한 것은 참조 관계를 쉽게 보여주기 위해서입니다. 출구가 하나뿐이라면 코어의 기본 출구 동작을 사용해도 됩니다. 다음 장의 설정 조각은 해당 계층에 합쳐야 합니다. 여러 최상위 객체를 파일 끝에 그대로 이어 붙이지 마세요.
{
"log": {
"loglevel": "warning"
},
"inbounds": [
{
"tag": "local-socks",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true
}
}
],
"outbounds": [
{
"tag": "direct",
"protocol": "freedom",
"settings": {}
}
],
"routing": {
"domainStrategy": "AsIs",
"rules": [
{
"type": "field",
"inboundTag": ["local-socks"],
"outboundTag": "direct"
}
]
}
}
객체·배열·태그 읽는 법
중괄호는 객체, 대괄호는 배열을 나타냅니다. 인바운드와 아웃바운드는 하나의 프로세스가 여러 진입점을 동시에 열거나 여러 출구를 가질 수 있어 보통 배열로 작성합니다. 포트는 숫자로, 스위치는 불리언 값으로 써야 합니다. true를 문자열로 작성하면 안 됩니다. JSON의 키와 문자열은 큰따옴표로 감싸며, 줄 끝의 불필요한 쉼표나 일반 주석은 허용되지 않습니다. 클라이언트 화면에서 입력할 수 있는 주석이나 메모가 코어 설정 필드에 포함되는 것은 아닙니다. 시작에 실패하면 먼저 문법 오류를 확인한 뒤 프로토콜 매개변수를 살펴보세요. 불필요한 설정 변경을 줄일 수 있습니다.
tag는 설정 안에서 사용하는 식별자이지 서버 주소나 화면에 표시되는 이름이 아닙니다. 라우팅의 outboundTag는 아웃바운드 중 하나의 태그와 정확히 일치해야 합니다. 대소문자, 공백, 하이픈이 달라져도 참조가 바뀝니다. 짧고 일관된 태그를 사용하고 수정한 뒤에는 모든 참조 위치를 확인하세요. 최상위 객체의 작성 순서는 대체로 실행 순서를 뜻하지 않지만, 배열 순서는 중요할 수 있습니다. 특히 라우팅 규칙과 기본 출구를 확인해야 합니다. 라우팅 문제를 해결하려고 최상위 키 순서만 바꾸지는 마세요.
설정을 내보내기 전에 현재 프록시 모드와 활성 서버를 기록하고, 내보낸 파일을 별도로 저장한 뒤 한 번에 하나씩 수정하세요. 파일에 구독 주소, 사용자 식별자, 인증 정보가 포함될 수 있습니다. 다른 사람에게 문제를 설명할 때는 관련 필드와 민감 정보를 가린 로그만 공유하세요. 설치 파일과 플랫폼 선택은 다운로드 페이지에서 확인할 수 있습니다. 아직 정상적으로 연결한 적이 없다면 빠른 시작 안내에 따라 기본 설정을 마친 뒤 돌아와 각 항목을 살펴보세요.
2. inbounds 인바운드 리스닝과 트래픽 탐지
리스닝 주소에 따라 접근 가능한 범위가 달라집니다
인바운드는 코어가 연결을 받는 진입점이지 원격 서버가 아닙니다. listen은 바인딩 주소, port는 포트, protocol은 인바운드 프로토콜을 지정합니다. 일반적인 단일 기기 사용에서는 127.0.0.1에 바인딩하는 것이 좋습니다. 로컬 루프백 네트워크에서 들어오는 연결만 받기 때문입니다. 0.0.0.0으로 변경하면 모든 IPv4 인터페이스에서 리스닝하므로 같은 네트워크의 다른 기기가 인바운드에 접근할 수 있습니다. 실제 접근 가능 여부는 방화벽, 네트워크 격리, 라우팅에도 영향을 받습니다. 로컬 네트워크에서 공유하려면 클라이언트에서 LAN 연결 허용을 켜는 데 그치지 말고 인증과 접근 범위도 함께 검토하세요.
포트가 일치하는 것만으로 연결이 보장되지는 않습니다. 앱에서 HTTP 프록시를 설정했다면 HTTP 인바운드에 연결해야 하고, SOCKS 프록시를 설정했다면 SOCKS 인바운드에 연결해야 합니다. 일부 클라이언트는 GUI에서 여러 프록시 프로토콜을 하나의 혼합 포트로 제공합니다. 여기서는 특정 클라이언트의 포트 구성을 모든 코어의 기본값으로 오해하지 않도록 인바운드를 따로 설명합니다. 매개변수 설정에서 실제 리스닝 포트를 확인한 뒤 앱에 프록시 주소를 입력하세요. 같은 주소와 포트를 사용하는 설정을 두 개 동시에 실행하지 마세요.
SOCKS·HTTP 인바운드 개별 예제
아래 조각은 최상위 inbounds 배열을 대체하며 SOCKS와 HTTP 진입점을 각각 제공합니다. SOCKS의 udp는 해당하는 UDP 전달 요청을 처리하도록 허용하지만, 앱도 SOCKS의 UDP 기능을 실제로 사용해야 하고 이후 출구도 해당 트래픽을 지원해야 합니다. 이 필드만 켠다고 모든 시스템 앱에서 UDP 프록시를 사용할 수 있는 것은 아닙니다. HTTP 인바운드는 HTTP 프록시를 직접 설정한 앱에 적합합니다. 앱이 시스템 프록시 설정을 따르는지는 앱 자체 동작에 달려 있습니다.
{
"inbounds": [
{
"tag": "local-socks",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true
},
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls"],
"routeOnly": true
}
},
{
"tag": "local-http",
"listen": "127.0.0.1",
"port": 10809,
"protocol": "http",
"settings": {}
}
]
}
sniffing은 연결 식별의 일부만 처리합니다
트래픽 탐지는 프로토콜 핸드셰이크에서 도메인 등의 정보를 추출하려고 시도합니다. 예를 들어 앱이 대상 IP만 전달해도 코어가 식별 가능한 HTTP 호스트 필드나 TLS 핸드셰이크에서 도메인을 알아내 도메인 규칙을 적용할 수 있습니다. destOverride는 탐지할 프로토콜 유형을 지정하는 필드이지, 코어가 임의의 내용을 복호화하게 하는 설정이 아닙니다. 암호화된 핸드셰이크, 앱 프로토콜, 코어 구현에 따라 식별할 수 있는 정보가 제한됩니다. 도메인을 탐지하지 못한다면 앱이 전달한 원래 대상과 IP 규칙부터 확인하세요. 모든 연결에 사용할 수 있는 도메인 정보가 있다고 가정하면 안 됩니다.
routeOnly는 탐지 결과를 연결 대상을 직접 바꾸지 않고 주로 라우팅 판단에 사용합니다. 이 설정으로 ‘규칙에 어떤 이름을 사용할지’와 ‘최종적으로 어디에 연결할지’를 구분할 수 있습니다. 지원 여부와 탐지 범위는 현재 코어를 기준으로 확인하세요. 코어가 필드를 인식하지 못한다고 이름이 비슷한 키를 임의로 만들어서는 안 됩니다. 탐지를 끈 뒤 도메인별 라우팅 결과가 바뀐다면 규칙이 탐지 정보에 의존할 수 있습니다. 서버 문제라고 단정하기보다 DNS 전달 방식과 앱이 대상 도메인을 유지하는지부터 확인하세요.
| 필드 | 확인할 사항 |
|---|---|
listen | 로컬에서만 사용하는지, LAN에서 공유하는지 확인하고 바인딩할 인터페이스가 있는지 살펴봅니다. |
port | 앱에 입력한 값과 실제 리스닝 값이 일치하는지, 포트를 다른 프로세스가 사용 중이지 않은지 확인합니다. |
protocol | 앱에서 사용하는 프록시 프로토콜과 인바운드 프로토콜이 일치하는지 확인합니다. |
sniffing | 도메인 식별이 필요한지, 현재 코어가 해당 필드를 지원하는지 확인합니다. |
시스템 프록시 스위치는 클라이언트가 운영체제에 적용하는 설정이며 여기서 다루는 인바운드 필드가 아닙니다. 시스템 프록시를 꺼도 SOCKS 주소를 직접 입력한 앱은 인바운드에 계속 연결될 수 있습니다. 시스템 프록시를 켜더라도 해당 설정을 따르지 않는 앱은 직접 인터넷에 연결할 수 있습니다. TUN은 가상 네트워크 어댑터, 라우팅 제어, 권한을 포함하므로 일반 SOCKS 인바운드를 추가하는 것으로 대체할 수 없습니다. 문제를 확인할 때는 앱이 어떤 방식으로 연결되는지 먼저 파악한 뒤 해당 진입점을 점검하세요. 서로 다른 세 가지 스위치를 동시에 변경하지 마세요.
3. outbounds 아웃바운드 프로토콜과 태그 연결
출구 정의와 라우팅 선택은 따로 확인하세요
아웃바운드는 연결을 다음 홉으로 전달합니다. protocol은 직접 연결, 차단, 특정 프록시 프로토콜 중 무엇을 사용할지 결정하고, settings에는 해당 프로토콜 전용 매개변수가 들어갑니다. streamSettings는 원격 서버와의 전송 방식 및 보안 계층을 정의합니다. 라우팅은 출구를 선택할 뿐 인증 정보를 자동으로 채우지 않습니다. 로그에서 프록시 태그가 선택된 것으로 보여도 서버 주소, 포트, 보안 매개변수가 잘못되면 연결에 실패합니다. ‘올바른 출구를 선택했는지’와 ‘출구가 연결을 정상적으로 맺었는지’를 별도의 확인 단계로 다루세요.
freedom은 현재 기기에서 대상에 직접 연결하고, blackhole은 해당 트래픽을 종료합니다. 둘 다 로컬 처리 방식이므로 구독에 등록된 원격 주소가 필요하지 않습니다. 아래 조각은 재사용할 수 있는 두 출구를 보여주며 1장의 인바운드와 함께 사용할 수 있습니다. 태그 이름에는 특별한 기능이 없습니다. 직접 연결 태그를 다른 이름으로 바꿔도 프로토콜 동작은 달라지지 않습니다. 실제 동작을 결정하는 것은 protocol입니다. 다만 태그를 바꾸면 라우팅, 프록시 체인 등 모든 참조도 함께 수정해야 합니다. 그렇지 않으면 설정이 시작되지 않거나 예상과 다르게 전달될 수 있습니다.
{
"outbounds": [
{
"tag": "direct",
"protocol": "freedom",
"settings": {}
},
{
"tag": "block",
"protocol": "blackhole",
"settings": {}
}
]
}
원격 아웃바운드 매개변수는 한꺼번에 확인하세요
실제 프록시 출구는 이미 보유한 서버 정보를 바탕으로 생성해야 합니다. 프로토콜마다 settings 구조가 다르므로 한 프로토콜의 사용자 필드를 다른 프로토콜 아래로 옮길 수 없습니다. VLESS 아웃바운드에는 보통 서버 목록, 주소, 포트, 사용자 식별자, 암호화 관련 설정이 들어갑니다. Trojan 아웃바운드는 인증 구조가 다릅니다. 서버 주소는 연결할 위치를, 사용자 식별자나 비밀번호는 인증 정보를, TLS 또는 REALITY 매개변수는 보안 핸드셰이크를 결정합니다. 각 필드가 서로 맞물리므로 값 하나만 바꿔도 전체 구성이 맞지 않을 수 있습니다.
이런 매개변수는 GUI 클라이언트의 서버 편집 화면에서 관리하는 편이 낫습니다. 신뢰할 수 있는 출처의 전체 공유 정보를 먼저 가져온 다음 내보낸 아웃바운드 객체와 비교하면 빈 파일에서 직접 작성하는 것보다 구성을 일관되게 유지하기 쉽습니다. 실제 서버 정보로 오해할 만한 계정 예제는 이 문서에 포함하지 않습니다. 유지관리 담당자에게 문제를 설명할 때는 프로토콜, 전송 방식, 보안 계층 사용 여부, 오류가 발생한 단계를 알리고 인증 정보는 제거하세요. 사용자 정보가 모두 포함된 아웃바운드 객체를 공개 게시판에 그대로 올리지 마세요.
기본 출구와 배열 순서
일반적인 단일 라우팅 설정에서는 어떤 규칙에도 일치하지 않는 연결에 아웃바운드 배열의 첫 번째 항목이 사용됩니다. 따라서 첫 번째 출구가 직접 연결인지 프록시인지에 따라 기본 처리 결과가 달라집니다. GUI의 ‘전역’, ‘LAN 우회’ 모드는 규칙을 다시 만들거나 출구 순서를 바꿀 수 있습니다. 모드 이름만으로 실제 설정을 판단하지 마세요. 내보낸 파일에서 첫 항목을 확인하고, 모든 연결에 적용되는 마지막 규칙이 있는지도 살펴보세요. 로드 밸런싱이나 아웃바운드 체인을 사용한다면 관련 참조를 따라가야 합니다. 이를 단일 출구 설정으로 단순화해서는 안 됩니다.
출구가 여러 개라고 모든 서버에 동시에 연결하는 것은 아닙니다. 일반적인 라우팅은 일치한 아웃바운드 태그 하나에 연결 처리를 맡깁니다. 서버 속도 측정, 자동 선택, 장애 조치는 클라이언트나 코어의 별도 기능이 있어야 합니다. 프록시 객체 두 개를 배열에 나란히 적는다고 속도에 따라 자동 선택되는 것은 아닙니다. 태그를 공유하면 참조가 모호해지거나 설정 오류가 생길 수 있으므로 논리적 출구마다 고유한 태그를 사용하세요. 여러 기기에서 동기화하려면 구독·QR 코드·설정 내보내기로 동기화하기를 참고해 서버 정보와 라우팅 설정의 동기화를 구분하세요.
직접 연결되는 최소 설정에서 확장하려면 정상 동작이 확인된 인바운드 하나를 유지하고 원격 출구 하나만 추가한 뒤 해당 출구를 지정하는 규칙을 하나 넣는 것이 좋습니다. 연결이 확인된 다음에만 분할 라우팅을 추가하세요. 이렇게 하면 인바운드, 프로토콜 매개변수, 규칙 문제를 따로 살펴볼 수 있습니다. 클라이언트가 사용자 설정과 내장 서버 모드에서 서로 다른 방식으로 실행된다면 현재 모드가 실제로 이 파일을 사용하는지 먼저 확인하세요. 로드되지 않는 설정을 편집해도 실행 중인 연결에는 영향을 주지 않습니다.
4. routing 규칙 순서와 일치 조건
규칙은 배열 순서대로 확인합니다
라우팅은 코어에 들어온 연결을 지정된 출구로 보냅니다. 흔히 쓰는 필드 규칙은 type: "field"를 사용해 대상 도메인, 대상 IP, 포트, 네트워크 유형, 인바운드 태그를 기준으로 연결을 구분합니다. 규칙은 일반적으로 위에서 아래 순서로 확인하며 사용 가능한 규칙이 일치하면 출구가 선택됩니다. 따라서 범위가 명확한 예외를 먼저 두고 적용 범위가 넓은 기본 규칙을 뒤에 배치하세요. 광범위한 규칙을 첫 줄에 두면 뒤에 있는 세부 규칙이 적용되지 않을 수 있습니다. 도메인이 규칙에 있는지만 검색하지 말고 앞선 규칙이 이미 연결을 가로채는지도 확인하세요.
한 규칙에 여러 조건을 작성하면 보통 모든 조건을 만족해야 합니다. 예를 들어 inboundTag와 domain을 함께 지정하면 지정된 인바운드에서 들어온 연결 중 해당 도메인과 일치하는 경우를 뜻합니다. 도메인 배열 안의 여러 값은 가능한 일치 대상을 여러 개 지정합니다. 서로 다른 필드의 조건을 ‘둘 중 하나’로 오해하지 마세요. 서로 연관 없는 두 조건을 각각 적용하려면 규칙을 두 개로 나누는 편이 명확합니다. 규칙에서 사용하는 인바운드 및 아웃바운드 태그는 실제로 정의되어 있어야 합니다. 설명용 주석은 태그 참조를 대신할 수 없습니다.
도메인 정확히 일치·접미사 일치·IP 대역
full:은 도메인 전체가 정확히 일치하는 경우에 사용하며 특정 호스트 이름만 처리할 때 적합합니다. domain:은 지정된 도메인과 하위 도메인까지 일치시켜 같은 도메인에 속한 여러 서비스를 처리할 때 적합합니다. regexp:는 정규식을 나타내며 패턴 인식이 꼭 필요한 경우에 사용합니다. 일반 문자열의 일치 범위도 코어 문서에서 확인해야 합니다. 정확히 일치한다고 단정하면 안 됩니다. 정규식을 JSON에 넣을 때는 역슬래시 이스케이프도 처리해야 합니다. 문법 오류 없이 입력해도 실제로 일치하지 않을 수 있습니다. 명확한 도메인으로 표현할 수 있다면 복잡한 정규식은 보통 필요하지 않습니다.
{
"routing": {
"domainStrategy": "AsIs",
"rules": [
{
"type": "field",
"domain": ["full:blocked.example"],
"outboundTag": "block"
},
{
"type": "field",
"ip": [
"127.0.0.0/8",
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
"::1/128",
"fc00::/7"
],
"outboundTag": "direct"
},
{
"type": "field",
"inboundTag": ["local-socks", "local-http"],
"network": "tcp,udp",
"outboundTag": "direct"
}
]
}
}
이 조각은 앞에서 설명한 직접 연결 및 차단 출구와 함께 사용합니다. 예제에서는 정확히 일치하는 도메인 규칙을 보여주기 위해 보존용 도메인을 사용합니다. 사설 주소 대역 규칙은 IP 조건을 설명하며, 마지막 규칙은 두 로컬 인바운드에서 들어온 나머지 연결을 직접 연결로 보냅니다. 프록시 분할 라우팅 설정은 아닙니다. 프록시를 기본 출구로 사용하려면 먼저 실제로 작동하는 프록시 출구를 추가하고 마지막 태그를 바꿔야 합니다. 도메인이 사설 주소를 가리킬 때 IP 규칙이 적용되는지는 해당 시점에 대상 IP가 있는지와 도메인 정책에서 조회를 수행하는지에 달려 있습니다.
domainStrategy와 규칙 데이터 파일
AsIs는 도메인 라우팅을 위해 별도로 조회하지 않고 연결에 이미 있는 정보를 최대한 사용합니다. IPIfNonMatch는 도메인이 일치하지 않으면 조회를 시도한 뒤 IP 조건을 확인합니다. IPOnDemand는 IP가 필요한 규칙을 만났을 때 조회를 시작할 수 있습니다. 뒤의 두 옵션은 DNS 의존성을 높이고 일치 결과를 바꿀 수 있습니다. 이 정책은 라우팅 판단을 위한 것으로 아웃바운드 연결 시의 조회 정책과 다르며 운영체제의 DNS 설정을 대신하지도 않습니다.
geoip:와 geosite: 형식은 사용할 수 있는 규칙 데이터 파일과 해당 파일에 분류 항목이 있는지에 따라 동작합니다. 설정 문자열을 올바르게 입력해도 클라이언트에 관련 데이터가 포함되어 있다고 보장할 수 없습니다. 시작할 때 파일이나 분류를 찾을 수 없다는 메시지가 뜨면 분류 이름을 비슷한 말로 바꾸기보다 코어 리소스 디렉터리와 클라이언트의 규칙 데이터 관리 메뉴를 먼저 확인하세요. 기기 간에 사용자 설정을 옮길 때도 외부 의존성을 함께 고려해야 합니다. JSON 파일 하나만 복사했다고 환경이 같아지는 것은 아닙니다.
규칙 문제를 찾을 때는 먼저 앱이 인바운드에 전달한 대상이 도메인인지 IP인지 기록하고, 트래픽 탐지로 도메인이 추가되었는지 확인한 뒤 규칙을 순서대로 검토하세요. 도메인 규칙 하나를 임시로 앞으로 옮기면 앞선 규칙이 연결을 가로채는지 확인하는 데 도움이 됩니다. 테스트가 끝나면 적절한 순서로 되돌려야 합니다. 규칙 수정은 보통 새 연결에만 적용됩니다. 브라우저나 앱이 기존 연결을 재사용하면 결과가 늦게 반영될 수 있습니다. 새 연결을 만들어 확인하고 기존 세션이 계속 작동하는 탓에 설정을 반복해서 바꾸지 마세요.
5. DNS 조회 경로와 분할 라우팅 범위
먼저 도메인을 누가 조회하는지 확인하세요
한 번의 접속에 운영체제 리졸버, 앱 자체 리졸버, 코어 내장 DNS, 원격 서버의 조회 과정이 관여할 수 있습니다. 최상위 dns는 코어가 직접 조회해야 할 때 사용할 방법을 정의할 뿐, 시스템의 모든 DNS 요청을 자동으로 가로채지는 않습니다. 앱이 먼저 도메인을 IP로 변환한 뒤 IP를 SOCKS 인바운드에 전달하면 코어가 받는 원래 대상은 더 이상 도메인이 아닙니다. 앱이 도메인을 프록시에 전달하면 이후 조회 위치가 달라질 수 있습니다. 문제를 해결할 때는 먼저 입력 형태를 확인하세요. 설정에 DNS 객체가 있다는 이유만으로 모든 조회가 그 객체를 거친다고 단정하면 안 됩니다.
아래 예제는 필드 구조를 보여주기 위해 일반 DNS 주소를 사용하며 특정 네트워크 환경에 가장 적합한 설정을 뜻하지 않습니다. servers는 DNS 서버 목록으로 간단한 주소나 일치 조건이 포함된 객체를 지정할 수 있습니다. hosts는 특정 이름을 주소에 정적으로 매핑하므로 임시 확인이나 고정 내부 서비스에 적합합니다. 서비스 주소가 바뀌어도 매핑은 자동으로 갱신되지 않습니다. 예제의 테스트 도메인과 문서용 주소는 형식을 설명하기 위한 것이므로 실제 웹사이트 설정에 그대로 남겨두지 마세요.
{
"dns": {
"hosts": {
"full:service.example": "192.0.2.10"
},
"servers": [
"1.1.1.1",
"8.8.8.8"
],
"queryStrategy": "UseIP"
}
}
조회 정책과 연결 정책은 다릅니다
queryStrategy는 코어의 DNS 조회에서 사용할 주소 유형을 제어합니다. UseIP는 IPv4와 IPv6 결과를 모두 허용하고 UseIPv4 또는 UseIPv6는 조회 범위를 제한합니다. 지원 여부는 현재 코어에서 확인해야 합니다. IPv4로 제한하면 일부 IPv6 경로 문제를 찾는 데 도움이 될 수 있지만 서버 핸드셰이크 오류를 해결하지는 못합니다. 반대로 기기 네트워크가 특정 주소 유형으로만 서비스에 연결할 수 있다면 조회 결과를 지나치게 제한해 원래 사용할 수 있었던 주소를 놓칠 수 있습니다.
라우팅의 도메인 정책, DNS의 조회 정책, 아웃바운드 자체의 도메인 조회 옵션은 서로 다른 단계에서 작동합니다. 라우팅이 IP 규칙을 적용하기 위해 먼저 조회한 뒤 아웃바운드 연결 시 자체 로직에 따라 대상을 처리할 수 있습니다. 두 단계의 정책이 다르면 확인되는 주소도 다를 수 있습니다. 먼저 ‘도메인으로 출구를 선택할지’ 아니면 ‘조회된 IP로 출구를 선택할지’를 정한 뒤 어느 단계에서 조회할지 결정하세요. 여러 위치의 정책 이름이 비슷하다는 이유만으로 같은 값으로 맞추지 마세요.
DNS 서버까지 연결할 경로도 필요합니다
일반 DNS 조회와 암호화된 DNS 요청 모두 DNS 서버에 도달해야 합니다. 코어마다 DNS 주소 형식, 직접 연결 모드, 조회 라우팅 방식이 다르므로 한 코어의 주소 형식을 다른 코어에 그대로 적용해서는 안 됩니다. 도메인으로 지정된 DNS 서버를 사용할 때는 DNS 서버에 처음 접속하려면 그 주소를 어떻게 조회할지도 고려해야 합니다. 이를 부트스트랩 조회라고 합니다. 특정 프록시를 통해서만 DNS 서버에 연결할 수 있는데 프록시 서버 주소도 같은 DNS 서버에 의존한다면 순환 참조가 생길 수 있습니다. 이 중 한 단계에는 별도로 도달 가능한 시작 경로를 마련해야 합니다.
DNS 서버 목록을 고정된 순서의 장애 조치 목록으로 이해하면 안 됩니다. 병렬 조회 여부, 대체 서버 사용 시점, 도메인 조건에 일치하는 서버가 기본 서버로 되돌아가는지 여부는 코어 구현과 객체 필드에 따라 달라집니다. 도메인별로 DNS 서버를 선택해야 한다면 간단한 설정에서 먼저 각 서버에 연결할 수 있는지 확인한 뒤 도메인 조건과 폴백 제어를 추가하세요. 여러 서버와 라우팅을 한 번에 바꾸면 시간 초과 원인을 파악하기 어렵습니다. 테스트할 때 조회 대상, 대상 주소 유형, 오류 발생 시간을 기록해 DNS 시간 초과와 연결 시간 초과를 구분하세요.
캐시도 테스트 결과에 영향을 줍니다. 앱이 도메인 조회 결과를 보관하거나 운영체제가 레코드를 캐시할 수 있으며 코어 내부에서도 조회 정보를 재사용할 수 있습니다. DNS를 바꿔도 기존 연결이 바로 다시 만들어지지 않아 이전 주소가 계속 나타날 수 있습니다. 테스트 앱의 기존 세션을 종료한 뒤 새 요청을 보내세요. 필요한 경우 클라이언트에서 제공하는 코어 재시작 기능으로 실행 상태를 초기화합니다. 웹페이지를 한 번 새로 고친 결과만으로 새 조회 정책이 완전히 적용됐다고 판단하지 마세요. 캐시로 인한 일시적인 차이를 라우팅 규칙이 임의로 실패하는 것으로 오해해서도 안 됩니다.
도메인으로 접속할 수 없지만 알고 있는 IP 주소로는 연결되더라도 인증서와 서버 이름 문제를 고려해야 합니다. 웹사이트 URL을 IP로 바꾸는 것이 동등한 테스트라고 볼 수는 없습니다. 먼저 DNS 응답을 확인하고, 선택된 출구를 확인한 뒤, 출구에서 대상 주소에 도달할 수 있는지 살펴보고 핸드셰이크 단계를 점검하세요. 각 단계를 기록하면 DNS 오류와 보안 계층 오류를 모두 ‘접속이 안 됨’으로 뭉뚱그리지 않을 수 있습니다.
6. policy 연결 타이머와 통계 설정
policy는 화면의 그룹이 아니라 사용자 레벨에 따라 적용됩니다
policy는 연결 수명과 일부 통계 동작을 관리하며 출구를 선택하거나 프로토콜 인증을 바꾸지 않습니다. levels의 키는 "0"처럼 문자열로 표시한 사용자 레벨입니다. 프로토콜에서 지원하는 사용자 레벨 필드가 해당 정책과 연결됩니다. 여기서 레벨은 구독 그룹, 서버 우선순위, 라우팅 목록 번호가 아닙니다. 레벨 객체 하나를 만들었다고 모든 프로토콜 객체가 자동으로 해당 레벨에 속하지는 않습니다. 내보낸 설정을 읽을 때 사용자 객체의 레벨과 코어의 프로토콜 지원 여부를 함께 확인하세요.
아래 예제는 레벨 정책과 시스템 통계 스위치의 구조를 보여줍니다. 수치는 필드를 설명하기 위한 것이며 모든 기기에 적합한 성능 설정을 뜻하지 않습니다. 일반적인 사용 환경에서는 우선 클라이언트 기본값을 유지하고 로그와 연결 상태에서 타이머 문제가 확인될 때만 조정하세요. 코어마다 기본값, 버퍼 할당, 통계 인터페이스가 다르므로 설정을 옮길 때 필드가 여전히 지원되는지 확인한 뒤 실제 동작을 비교해야 합니다. 네트워크 설정을 모든 기기에 복사해도 같은 결과가 보장되지는 않습니다.
{
"policy": {
"levels": {
"0": {
"handshake": 4,
"connIdle": 300,
"uplinkOnly": 2,
"downlinkOnly": 5,
"statsUserUplink": false,
"statsUserDownlink": false
}
},
"system": {
"statsInboundUplink": false,
"statsInboundDownlink": false,
"statsOutboundUplink": false,
"statsOutboundDownlink": false
}
}
}
타이머 네 개는 서로 다른 단계를 제한합니다
handshake는 인바운드 프로토콜 핸드셰이크 시간 제한을 초 단위로 지정합니다. 웹페이지 전체 로딩 시간이나 모든 원격 TLS 핸드셰이크를 제어하는 공통 스위치가 아닙니다. 핸드셰이크 전에 로컬 포트에 연결할 수 없는 경우 이 값을 늘려도 도움이 되지 않습니다. 원격 아웃바운드가 인증 오류로 실패하는 경우에도 인바운드 핸드셰이크 시간을 늘려 해결할 수 없습니다. 로그를 통해 어느 단계에서 멈췄는지 먼저 확인한 뒤 시간 여유를 늘릴 필요가 있는지 판단하세요.
connIdle은 데이터가 오가지 않는 동안 연결을 유지하는 시간을 제한합니다. 값을 너무 짧게 설정하면 간헐적으로 데이터를 보내는 장기 연결에 영향을 줄 수 있고, 너무 길게 설정하면 유휴 연결이 더 오래 리소스를 차지할 수 있습니다. 앱의 하트비트, 서버의 유휴 연결 정책, 중간 네트워크 장비의 시간 제한도 연결 유지에 영향을 줍니다. 클라이언트 타이머만 늘려 다른 단계의 제한을 해결할 수는 없습니다. 주기적으로 연결이 끊긴다면 종료 전에 데이터가 오갔는지, 클라이언트와 서버 양쪽에 서로 다른 시간 제한이 있는지 기록하세요.
uplinkOnly와 downlinkOnly는 연결 한쪽이 종료된 뒤 반대 방향의 전송이 계속될 수 있는 대기 단계를 처리합니다. 이런 반쯤 닫힌 상태는 연결 전체가 유휴 상태인 것과 다릅니다. 업로드가 끝나도 다운로드가 끝난 것은 아닐 수 있고, 다운로드가 끝나도 앱의 모든 전송이 완료됐다는 뜻은 아닙니다. 값을 지나치게 낮추면 마무리 중인 전송이 끊길 수 있고 무턱대고 늘리면 불필요한 세션이 오래 남을 수 있습니다. 대용량 파일이나 요청 종료 단계에서 문제가 발생하면 앱 프로토콜과 로그를 함께 살펴보세요. 이 두 필드는 업로드·다운로드 속도 제한이 아닙니다.
통계를 켜도 화면에 바로 표시되는 것은 아닙니다
사용자 통계를 수집하려면 해당 사용자 식별자와 코어 지원이 필요합니다. 시스템 통계는 인바운드와 아웃바운드 객체를 기준으로 계산합니다. true로 설정하면 해당 통계 수집이 허용될 뿐, 조회 인터페이스가 자동으로 만들어지거나 클라이언트에 통계 화면이 추가되지는 않습니다. 일부 환경에서는 최상위 통계 모듈, API 서비스, 호출 측 설정도 필요합니다. GUI 클라이언트가 이미 관련 설정을 관리할 수 있으므로 API 인바운드나 통계 서비스를 직접 중복 추가하면 포트 충돌이 생길 수 있습니다. 일상적인 연결 정보를 확인하려면 우선 클라이언트의 기존 로그와 상태 화면을 사용하세요.
policy의 버퍼 관련 필드를 일반적인 속도 향상 스위치로 봐서는 안 됩니다. 버퍼 크기, 동시 연결 수, 기기 메모리, 프로토콜 동작이 함께 리소스 사용량을 결정합니다. 버퍼를 늘리면 메모리 부담이 커질 수 있지만 원격 회선 용량이 증가하지는 않습니다. 재현 가능한 병목이 있고 코어 문서에 근거가 있는 경우가 아니라면 기본값을 유지하세요. 특히 모바일 기기에서는 백그라운드 제한이나 절전 설정이 연결 중단의 원인일 수 있습니다. 코어 policy 설정으로 운영체제의 백그라운드 실행 권한을 대신할 수는 없습니다.
policy를 수정할 때는 한 번에 하나만 변경하고 기존의 정상 설정을 보관하세요. 짧은 요청, 지속적인 전송, 유휴 상태 후 재개 등 세 가지 동작을 테스트해 각각 이상이 생기는지 확인합니다. 클라이언트가 설정 파일을 다시 만든다면 임시 실행 파일을 반복해서 수정하지 말고 지원되는 고급 설정 메뉴에 저장하세요. 설정이 시작된다는 것은 문법과 일부 의미를 코어가 받아들였다는 뜻일 뿐, 타이머가 현재 앱에 적합하다는 보장은 아닙니다. 실제 동작으로 한계를 확인해야 합니다.
7. 전송·보안 계층과 코어별 차이
프록시 프로토콜 외에 전송 조합도 있습니다
서버 항목에는 보통 프록시 프로토콜, 전송 방식, 보안 계층이 함께 설정됩니다. 프록시 프로토콜은 인증과 프록시 동작을, 전송 방식은 데이터를 전달하는 방법을, 보안 계층은 관련 핸드셰이크 보호를 담당합니다. 화면에서는 서로 가까이 표시될 수 있지만 같은 필드는 아닙니다. 아웃바운드의 protocol과 streamSettings.network는 각각 다른 역할을 합니다. 화면의 전송 유형을 프록시 프로토콜 위치에 넣으면 잘못된 구조가 됩니다. 특정 설정이 한 클라이언트에서만 작동한다면 이 세 계층을 비교한 뒤 코어가 해당 문법을 지원하는지도 확인하세요.
아래는 WebSocket과 TLS 전송 설정 조각입니다. 실제 서버 매개변수가 있는 아웃바운드 객체 안에 넣어야 하며 JSON 최상위에 바로 추가하면 안 됩니다. 예제의 보존용 도메인은 필드 위치를 보여주기 위한 것이며 실제로 연결할 수 있는 서버 정보가 아닙니다. 서버 측 전송 방식, 경로, 인증서 이름과도 일치해야 합니다. 아웃바운드 프로토콜과 인증 설정이 올바르지 않다면 이 조각만 복사해도 정상 연결되지 않습니다.
{
"streamSettings": {
"network": "ws",
"security": "tls",
"tlsSettings": {
"serverName": "edge.example",
"allowInsecure": false
},
"wsSettings": {
"path": "/connect",
"headers": {
"Host": "edge.example"
}
}
}
}
주소·serverName·Host는 서로 다릅니다
아웃바운드 서버 주소는 네트워크로 연결할 호스트를 지정합니다. serverName은 TLS 핸드셰이크와 인증서 이름 확인에 사용되고, WebSocket의 Host는 HTTP 핸드셰이크의 호스트 정보를 나타냅니다. 일부 서버에서는 세 값이 같지만 서버 측 요구 사항에 따라 다를 수도 있습니다. 보기 좋게 정리하려고 모두 같은 값으로 바꾸지 마세요. 인증서 오류가 발생하면 먼저 기기의 시간, 서버 이름, 서버 인증서 설정을 확인하세요. 인증서 검증을 끄지 마세요. 검증을 끄면 연결 신뢰 조건이 달라지고 잘못된 대상을 설정한 문제를 숨길 수 있습니다.
WebSocket 경로는 서버의 전송 진입점과 일치해야 하며 대소문자와 경로 내용도 서버 설정을 따라야 합니다. 웹 브라우저 주소창에 입력하는 임의의 경로가 아니며 다른 서버 항목에서 그대로 가져와서도 안 됩니다. 일반 웹페이지 같은 응답이 오거나 핸드셰이크 상태 오류가 발생하면 같은 호스트의 다른 서비스에 연결했을 수 있습니다. 이때는 사용자 식별자만 계속 바꾸지 말고 전송 방식, 경로, 호스트 필드를 확인하세요. 일반적인 포트 연결 테스트는 포트가 응답한다는 사실만 보여줍니다. 그 포트에서 올바른 프록시 프로토콜 조합이 작동한다는 뜻은 아닙니다.
REALITY와 코어 선택
REALITY는 보안 계층과 관련된 기능이지 최상위 프록시 프로토콜 이름이나 임의로 켤 수 있는 범용 강화 스위치가 아닙니다. 사용하려면 실제 코어의 지원 여부와 서버가 제공한 서버 이름, 공개 키 자료, 짧은 식별자 및 기타 필수 매개변수를 확인해야 합니다. 코어나 설정 세대에 따라 필드 표기가 다를 수 있으므로 키 이름을 추측해서 호환성 문제를 해결하면 안 됩니다. 이 문서에서는 서버 정보 없이 사용할 수 있는 REALITY 범용 템플릿을 제공하지 않습니다. 클라이언트가 올바르게 인식하는 전체 공유 정보를 가져와 실제 생성된 아웃바운드 객체와 비교하세요.
v2rayN의 구체적인 기능은 선택한 코어와 클라이언트 연동에 따라 달라집니다. v2rayNG와 v2flyNG도 화면이 비슷하다고 해서 완전히 같은 클라이언트로 볼 수 없습니다. 특정 프로토콜이나 보안 계층이 필요하다면 해당 코어에서 지원하는지 먼저 확인한 뒤 클라이언트를 선택하세요. 코어를 바꾸면 기존 설정의 필드가 거부되거나 기본 동작이 달라질 수 있습니다. 원본 설정을 저장하고 클라이언트에서 지원하는 방법으로 다시 생성한 뒤 주요 매개변수를 비교하세요. 자세한 내용은 Xray 코어와 V2Fly 코어의 차이를 참고하세요.
| 계층 | 주요 확인 사항 |
|---|---|
| 프록시 프로토콜 | 프로토콜 유형, 사용자 인증 정보, 프로토콜 전용 설정 |
| 전송 방식 | 전달 유형, 경로, 전송 핸드셰이크 필드가 서버 설정과 일치하는지 확인합니다. |
| 보안 계층 | 보안 유형, 서버 이름, 필요한 인증 자료 |
| 코어 구현 | 해당 조합을 지원하는지, 필드 구조가 일치하는지 확인합니다. |
Mux 다중화는 연결을 구성하는 별도의 기능입니다. 보안 핸드셰이크나 인증 오류를 해결하지 않으며 모든 앱에서 성능이 향상되는 것도 아닙니다. 클라이언트의 Mux 스위치는 생성되는 아웃바운드 설정에 영향을 줄 수 있으므로 사용 전에 프로토콜 조합과 코어 제한을 확인하세요. 문제를 해결할 때는 먼저 추가 연결 옵션을 끄고 기본 구성으로 테스트한 뒤 하나씩 다시 켜세요. 프로토콜 연결이 되지 않는 상태에서 Mux, DNS, 라우팅, 탐지를 동시에 변경하지 마세요. 우연히 문제가 해결되더라도 원인을 파악하기 어렵습니다.
플랫폼을 옮길 때는 운영체제 환경도 확인해야 합니다. Windows, macOS, Linux의 시스템 프록시 방식은 서로 다릅니다. Android의 VPN 연결과 앱별 선택도 JSON 전송 필드에 포함되지 않습니다. 전송 설정이 올바르더라도 앱 트래픽이 인바운드에 들어오지 않으면 예상한 연결이 생기지 않습니다. macOS에서 처음 실행할 때 시스템 허용이나 네트워크 권한이 필요하다면 설치 허용 및 네트워크 권한 안내를 확인하세요. 시스템 권한 문제를 프로토콜 매개변수 수정으로 해결하려고 하지 마세요.
8. 설정 검증·로그 확인·복구
문법·시작·인바운드·아웃바운드 순으로 확인하세요
설정 문제를 해결하려면 먼저 복구할 수 있는 정상 설정 사본을 확보해야 합니다. 현재 클라이언트 설정과 사용자 설정을 저장한 뒤 한 번에 한 계층만 수정하고 해당 코어를 다시 시작한 다음 새 연결을 만드세요. 시작할 수 없다면 의미 있는 첫 번째 오류를 확인하세요. 쉼표 누락은 JSON 문법 오류, 인식하지 못하는 필드나 프로토콜은 설정 호환성 문제, 리스닝 실패는 주소나 포트 충돌인 경우가 많습니다. 이러한 문제는 정상적인 트래픽 전달보다 먼저 발생하므로 원격 서버 속도나 웹사이트 접근 여부부터 조사할 필요는 없습니다. 가장 먼저 발생한 오류를 고친 뒤 후속 로그를 확인해 연쇄 오류를 쫓지 않도록 하세요.
아래 조각은 최상위 로그 객체를 임시로 대체해 진단 정보를 늘릴 때 사용할 수 있습니다. 로그 수준을 debug로 설정하면 기록이 많아지고 대상 주소나 연결 특성이 포함될 수 있습니다. 문제를 해결한 뒤에는 일상 사용에 적합한 수준으로 되돌리세요. 예제는 파일 경로를 지정하지 않으며 출력 위치는 실행 방법과 클라이언트에서 관리합니다. 화면에 추가 로그가 나타나지 않으면 로그 모듈이 고장 났다고 바로 판단하지 말고 클라이언트가 해당 로그 수준을 지원하는지, 실행 중인 프로세스가 수정된 파일을 불러왔는지 확인하세요.
{
"log": {
"loglevel": "debug"
}
}
오류가 발생한 단계에 따라 범위를 좁히세요
코어가 정상적으로 시작되면 먼저 앱에서 인바운드 연결이 실제로 발생하는지 확인하세요. 관련 기록이 없다면 앱의 프록시 주소와 프로토콜, 시스템 프록시 모드, 앱별 설정을 살펴봅니다. 연결 기록이 있지만 잘못된 출구가 선택됐다면 태그, 규칙 순서, 대상 형식을 확인하세요. 출구는 올바른데 연결에 실패한다면 원격 주소, 인증, 전송 방식, 보안 계층을 점검합니다. 연결은 되지만 일부 도메인만 문제가 있다면 DNS, 도메인 일치 조건, 대상 서비스 순서로 확인하세요. 이런 순서를 따르면 모든 문제를 ‘노드가 작동하지 않음’으로 단정하지 않을 수 있습니다.
포트 연결이 거부되면 대상 포트에서 리스닝하지 않거나 연결을 명시적으로 거부한다는 뜻인 경우가 많으며 DNS 실패와는 다릅니다. DNS 시간 초과는 주소를 얻기 전에 발생합니다. 핸드셰이크 실패는 특정 프로토콜 협상 단계에 진입했다는 뜻이지만 구체적인 원인은 로그와 함께 확인해야 합니다. 시간 초과만으로 원인을 특정할 수는 없습니다. 연결 설정, 프로토콜 핸드셰이크, 응답 대기 중 어느 단계에서든 발생할 수 있습니다. 다른 사람에게 상황을 설명할 때는 막연한 오류 한 줄만 전달하지 말고 발생 단계와 재현 방법을 명시하세요. 로그에 기록된 시간은 작업과 해당 기록을 연결하는 데 도움이 됩니다.
원인을 확인할 수 있는 최소 재현 환경 만들기
활성 서버 하나를 선택하고 명확한 인바운드 하나와 단순한 출구 규칙 하나만 사용해 추가 분할 라우팅을 잠시 제거하세요. 기본 연결을 확인한 뒤 도메인 규칙, IP 규칙, DNS 조건, 고급 정책을 차례로 복원합니다. 특정 계층을 복원한 뒤 문제가 발생하면 그 계층에서 관련 조건만 남겨 계속 확인하세요. 설정이 규칙 데이터 파일에 의존한다면 파일이 있는지와 분류 항목이 유효한지도 함께 알려야 합니다. 관련 없는 규칙이 잔뜩 포함된 전체 내보내기 파일을 처음부터 문제 해결 자료로 사용하지 마세요. 정보가 너무 많으면 실제 원인이 가려질 수 있습니다.
클라이언트가 설정을 다시 만드는 것은 흔히 놓치는 변수입니다. 수정 직후에는 작동하다가 서버를 바꾸면 사라진다면 임시 파일을 편집했거나 현재 모드에서 사용자 설정이 저장되지 않은 것일 수 있습니다. 클라이언트가 지원하는 설정 메뉴에서 저장 위치를 확인하고 다시 내보낸 파일과 비교하세요. 구독 업데이트는 구독에서 관리하는 서버 정보를 갱신하는 기능일 뿐, 수동으로 입력한 모든 필드가 유지된다고 볼 수는 없습니다. 메인 화면의 메뉴는 서버 목록·로그 패널·매개변수 설정 빠른 안내를, 설정 구조는 config.json 항목별 해설을 참고하세요.
자주 묻는 경계 사례
설정은 시작되는데 앱이 프록시를 사용하지 않는 이유는 무엇인가요?
시작에 성공했다는 것은 코어가 실행 상태에 들어갔다는 뜻일 뿐입니다. 앱이 시스템 프록시, 직접 설정한 프록시, 클라이언트의 트래픽 가로채기 중 하나를 통해 해당 인바운드로 연결되는지 확인한 다음 라우팅이 예상한 출구를 선택하는지 살펴보세요. 먼저 인바운드 기록이 있는지 확인하고 그다음 프로토콜 설정을 점검하세요.
규칙을 수정했는데 이미 열린 웹페이지 연결은 왜 바뀌지 않나요?
기존 연결은 원래 출구와 주소를 계속 사용할 수 있습니다. 테스트 앱의 이전 세션을 종료하고 새 연결을 만든 뒤 새 로그를 확인하세요. 필요한 경우 코어를 다시 시작할 수 있지만 재시작만으로 규칙 확인을 대신해서는 안 됩니다.
복구할 때는 마지막으로 정상 작동한 클라이언트 설정이나 전체 설정을 되돌린 뒤 코어를 다시 시작하고 활성 서버, 프록시 모드, 인바운드 포트를 확인하세요. 그래도 문제가 남으면 이번 JSON 수정뿐 아니라 시스템 네트워크, 권한, 서버 상태도 원인일 수 있습니다. 기본 작업 순서는 빠른 시작에서 다시 확인하세요. 기기에 맞는 클라이언트를 다시 선택하려면 다운로드 페이지를 이용하세요. 오류가 발생한 단계를 확인하는 대신 클라이언트만 계속 바꾸지 마세요.