JSON 최상위 구조와 설정 읽기 순서
먼저 데이터 형식, 태그 참조와 처리 체인을 확인한 다음 개별 프로토콜 매개변수를 살펴보세요.
최상위 객체는 실행 단계 목록이 아닙니다
V2Ray 설정 파일은 하나의 JSON 객체를 루트 노드로 사용하며, 일반적인 최상위 필드에는 log, dns, inbounds, outbounds, routing, policy와 stats가 포함됩니다. 파일에서 이 필드를 작성한 순서는 처리 순서를 바꾸지 않습니다. 커널은 설정을 로드할 때 먼저 전체 객체를 해석한 다음 수신 포트, 아웃바운드 객체, 라우터와 이름 해석기를 구성합니다. routing을 inbounds보다 앞에 작성해도 라우팅이 인바운드보다 먼저 실행되지는 않습니다. 실제 연결 관계를 결정하는 것은 태그와 규칙 참조입니다.
inbounds와 outbounds는 모두 배열입니다. 하나의 프로세스가 여러 로컬 포트를 동시에 수신하고 여러 출구를 유지할 수 있기 때문입니다. 배열의 각 객체에는 보통 tag가 포함됩니다. 라우팅 규칙은 inboundTag, outboundTag 또는 balancerTag로 이러한 태그를 참조합니다. 태그는 동일한 설정 안에서 고유해야 하며 socks-in, proxy, direct, block처럼 짧고 안정적인 이름을 사용하세요. 태그를 변경했다면 모든 참조를 함께 확인해야 합니다. 그렇지 않으면 설정은 해석되더라도 라우팅을 구성할 때 대상을 찾을 수 없다는 오류가 발생할 수 있습니다.
읽기 쉬운 최소 구조
{
"log": {
"loglevel": "warning"
},
"inbounds": [
{
"tag": "socks-in",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true
}
}
],
"outbounds": [
{
"tag": "direct",
"protocol": "freedom"
},
{
"tag": "block",
"protocol": "blackhole"
}
],
"routing": {
"domainStrategy": "AsIs",
"rules": [
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "direct"
}
]
}
}
이 예제는 로컬 SOCKS 인바운드와 두 개의 기본 아웃바운드만 구성하며 원격 프록시 연결은 설정하지 않습니다. 구조의 계층을 살펴보기에 적합합니다. 프로토콜 전용 매개변수는 해당 객체의 settings에, 전송 계층 매개변수는 대개 streamSettings에 넣고, 연결 재사용 같은 확장 기능은 별도의 하위 객체를 사용합니다. 다른 계층의 필드를 settings로 옮기지 마세요. JSON 문법이 올바르더라도 커널이 해당 객체에 속하지 않는 필드를 무시하거나 역직렬화 오류를 보고할 수 있습니다.
객체, 배열과 데이터 형식
설정 오류는 프로토콜 문제가 아니라 JSON 데이터 형식 불일치인 경우가 많습니다. 포트는 보통 숫자이므로 10808로 작성해야 하며 문자열 "10808"로 임의 작성하면 안 됩니다. 불리언 스위치는 true 또는 false를 사용하고 따옴표로 감싼 문자열로 작성하지 않습니다. 규칙의 도메인과 IP 조건은 대개 배열을 사용하므로 항목이 하나뿐이어도 대괄호를 유지해야 합니다. 객체 속성 사이에는 쉼표가 필요하지만 마지막 속성 뒤에는 불필요한 쉼표를 둘 수 없습니다. 전각 따옴표, 보이지 않는 공백과 서식 있는 텍스트에서 복사한 굽은 따옴표도 정상으로 보이는 위치에서 파서 오류를 일으킬 수 있습니다.
클라이언트는 설정을 생성할 때 구독 노드, 라우팅 프리셋과 로컬 포트를 최종 파일로 합치는 경우가 많습니다. v2rayN은 Windows, macOS와 Linux에서 이러한 데스크톱 설정을 확인하고 조정하는 데 적합합니다. Android의 v2rayNG와 v2flyNG는 보통 인터페이스에서 설정을 저장한 뒤 시작할 때 실행 설정을 생성합니다. 인터페이스의 이름이 하위 필드와 항상 동일한 것은 아닙니다. 예를 들어 '로컬 네트워크 우회'는 최종적으로 geoip:private 직결 규칙으로 나타날 수 있습니다. 확인할 때는 인터페이스 스크린샷이 아니라 클라이언트가 실제로 내보낸 실행 설정과 로그를 기준으로 삼으세요.
유지 관리하기 쉬운 읽기 방법
긴 설정을 읽을 때는 먼저 모든 인바운드와 아웃바운드 태그를 나열하고, 라우팅 규칙을 따라 대상 태그를 하나씩 추적한 뒤 DNS와 전송 매개변수를 확인하세요. 깊은 하위 필드 하나만 보고 전체 연결 동작을 추측하지 마세요. 요청은 먼저 인바운드로 들어오며 대상 도메인, 대상 IP, 포트와 프로토콜 식별 결과를 가질 수 있습니다. 라우터는 조건에 따라 아웃바운드를 선택하고, 아웃바운드는 프로토콜과 전송 설정에 따라 대상에 연결합니다. DNS는 규칙 대조나 아웃바운드 연결 단계에 관여할 수 있으며 policy는 연결 통계, 유휴 타임아웃과 세션 동작에 영향을 줍니다. 이 흐름대로 읽으면 '설정을 로드할 수 있음'과 '연결을 완료할 수 있음'을 구분할 수 있습니다.
inbounds 인바운드: 로컬 수신과 트래픽 진입점
인바운드는 어떤 애플리케이션이 접속할 수 있는지, 어떤 로컬 프로토콜을 사용하는지, 어떤 식별 정보와 함께 라우터로 들어오는지를 결정합니다.
listen, port와 노출 범위
인바운드 객체에서 먼저 확인할 것은 listen과 port입니다. 데스크톱 클라이언트가 로컬 애플리케이션에 프록시를 제공할 때는 보통 127.0.0.1에서 수신하며, 이는 현재 기기에서만 연결할 수 있음을 뜻합니다. 수신 주소를 외부 네트워크 인터페이스에 노출되는 주소로 바꾸면 접근 범위가 달라지고 로컬 네트워크 경계, 시스템 방화벽과 인증을 함께 고려해야 합니다. 브라우저, 터미널 또는 로컬 소프트웨어에만 프록시를 제공한다면 수신 범위를 넓힐 필요가 없습니다.
포트는 다른 프로세스가 사용하고 있지 않아야 합니다. 동일한 설정의 두 인바운드도 같은 주소에서 같은 포트를 수신할 수 없습니다. 일반적인 클라이언트는 SOCKS와 HTTP 인바운드를 각각 만들거나 혼합 인바운드를 지원합니다. 시스템 프록시는 보통 HTTP 인바운드를 사용하며, SOCKS를 명시적으로 지원하는 애플리케이션은 SOCKS 포트를 직접 입력할 수 있습니다. 애플리케이션이 HTTP 요청을 SOCKS 포트로 보내면 로그에 핸드셰이크를 인식하지 못했다는 메시지, 연결 종료 또는 잘못된 요청 형식이 나타날 수 있습니다. 이는 원격 노드 문제가 아니라 로컬 인바운드 프로토콜을 잘못 입력한 경우입니다.
SOCKS 인바운드와 UDP
{
"tag": "socks-in",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true,
"ip": "127.0.0.1"
},
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls"],
"routeOnly": true
}
}
auth는 SOCKS 인바운드의 인증 방식을 제어하며, 루프백 주소에서만 수신할 때는 noauth를 자주 사용합니다. udp는 SOCKS UDP 요청을 받을지 결정합니다. 이 옵션을 켠다고 해서 모든 애플리케이션이 자동으로 UDP를 프록시로 전달하는 것은 아닙니다. 애플리케이션 자체가 SOCKS UDP를 지원해야 하며 원격 프로토콜과 전송 경로도 해당 트래픽을 처리할 수 있어야 합니다. ip는 UDP 연관 응답 주소에 사용되므로 실제 접근 방식과 일치해야 합니다. 로컬 인바운드에는 루프백 주소를 입력하는 것이 직관적이지만, 다른 기기를 대상으로 한다면 해당 주소에 요청 측이 접근할 수 있는지 다시 확인해야 합니다.
HTTP 인바운드와 시스템 프록시
{
"tag": "http-in",
"listen": "127.0.0.1",
"port": 10809,
"protocol": "http",
"settings": {
"allowTransparent": false
},
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls"],
"routeOnly": true
}
}
HTTP 인바운드는 일반 프록시 요청과 CONNECT 터널 요청을 처리하므로 브라우저 및 시스템 프록시 설정을 따르는 소프트웨어에 적합합니다. 이는 웹사이트 서버가 사용하는 HTTP 서비스와 다릅니다. 애플리케이션이 해당 포트가 프록시 포트임을 명확히 인식해야 합니다. 터미널 명령, 개발 도구와 백그라운드 서비스는 데스크톱 시스템 프록시를 읽지 않을 수 있으므로 브라우저가 연결된다고 해서 모든 프로그램이 이 인바운드를 거친다고 볼 수 없습니다. '웹페이지는 정상인데 명령줄은 직접 연결되는' 경우 먼저 대상 프로그램의 프록시 옵션이나 환경 변수를 확인한 다음 V2Ray 설정을 점검하세요.
sniffing의 역할과 한계
트래픽 스니핑은 일부 연결에서 대상 도메인을 복원하여 도메인 라우팅 규칙이 적용될 가능성을 높입니다. TLS 연결을 예로 들면 애플리케이션이 로컬에서 도메인을 먼저 해석한 뒤 IP 주소로 연결할 수 있습니다. 이때 인바운드가 IP만 확인하면 단순한 도메인 규칙은 일치하지 않을 수 있습니다. sniffing을 활성화하면 커널이 핸드셰이크 정보에서 대상 도메인을 식별할 수 있습니다. destOverride는 식별을 허용할 프로토콜 유형을 지정하고, routeOnly는 식별 결과를 주로 라우팅 판단에 사용하며 최종 연결 대상을 강제로 바꾸지는 않습니다.
스니핑은 범용 복호화 기능이 아니며 모든 연결에서 식별 가능한 도메인을 보장하지도 않습니다. 비표준 프로토콜, 암호화 핸드셰이크 변경, 애플리케이션의 자체 캡슐화 또는 IP 직접 접속에서는 IP 조건만 남을 수 있습니다. 라우팅을 구성할 때는 도메인 규칙과 IP 규칙을 함께 고려해야 하며, 모든 판단을 스니핑 결과에 의존해서는 안 됩니다. 특정 애플리케이션에서 스니핑 활성화 후 이상이 발생하면 해당 인바운드에서 스니핑을 끄거나 별도의 인바운드 태그로 분리해 입구별 라우팅 규칙을 적용하세요.
여러 인바운드의 협업 방식
여러 인바운드의 가장 유용한 용도는 트래픽 출처를 구분하는 것입니다. 예를 들어 브라우저에는 http-in을, 개발 도구에는 socks-in을 할당한 뒤 inboundTag로 두 인바운드에 서로 다른 라우팅을 적용할 수 있습니다. 이는 도메인만 보고 애플리케이션 출처를 추측하는 것보다 안정적입니다. 각 인바운드에는 명확한 태그를 지정하고 클라이언트 인터페이스, 시스템 프록시와 애플리케이션 설정에서 포트를 일치시켜야 합니다. v2rayN에서 로컬 포트를 변경해도 기존 시스템 프록시 값이나 터미널 환경 변수가 반드시 함께 바뀌지는 않습니다. 문제를 해결할 때는 수신 로그와 애플리케이션 측 설정을 모두 확인하세요.
| 인바운드 유형 | 일반적인 용도 | 우선 확인할 항목 |
|---|---|---|
| SOCKS | SOCKS를 지원하는 브라우저, 터미널과 개발 도구 | 프로토콜 유형, 포트, UDP 지원 |
| HTTP | 시스템 프록시와 HTTP 프록시를 지원하는 소프트웨어 | CONNECT 지원, 시스템 프록시 포트 |
| 독립 인바운드 | 애플리케이션 유형별로 서로 다른 라우팅 적용 | 태그 고유성 및 inboundTag 규칙 |
outbounds 아웃바운드: 프로토콜, 서버와 전송 계층
아웃바운드는 요청이 최종적으로 어디에서 나가고 원격에 연결할 때 어떤 프로토콜, 인증 매개변수와 전송 방식을 사용하는지 설명합니다.
아웃바운드 태그와 세 가지 기본 목적지
실용적인 설정에는 보통 프록시, 직결과 차단이라는 세 가지 아웃바운드가 최소한 포함됩니다. 프록시 아웃바운드는 구독이나 수동 설정의 원격 서버에 연결하고, freedom 아웃바운드는 대상에 직접 접속하며, blackhole 아웃바운드는 일치한 트래픽을 종료합니다. 각각 proxy, direct, block 같은 태그를 사용할 수 있습니다. 태그 자체에는 특별한 의미가 없고 실제 동작은 protocol이 결정하지만, 안정적인 이름은 라우팅 규칙을 읽기 쉽게 합니다.
배열 순서는 라우팅 규칙이 일치하지 않을 때 사용할 기본 출구에 영향을 줄 수 있습니다. 모호한 기본값에 의존하지 않으려면 주요 프록시 아웃바운드를 명확한 위치에 두고, 로컬 네트워크·차단 목록·직결 대상에 대한 규칙을 명시적으로 작성하는 것이 좋습니다. 클라이언트는 설정을 생성하면서 DNS 전용 출구나 체인 프록시 중간 출구 같은 추가 아웃바운드를 넣을 수 있습니다. 수동 편집 전에 이러한 태그가 다른 객체에서 참조되는지 확인하고 이름만 보고 삭제하지 마세요.
VLESS 아웃바운드 예제
{
"tag": "proxy",
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "server.example.com",
"port": 443,
"users": [
{
"id": "00000000-0000-4000-8000-000000000000",
"encryption": "none",
"flow": ""
}
]
}
]
},
"streamSettings": {
"network": "tcp",
"security": "tls",
"tlsSettings": {
"serverName": "server.example.com",
"allowInsecure": false
}
}
}
address는 원격 서버 주소이며 도메인이나 IP를 사용할 수 있습니다. port는 원격 수신 포트입니다. users 안의 id는 인증 식별자로 서버 설정과 일치해야 합니다. VLESS의 encryption은 프로토콜 요구에 따라 지정된 값을 입력하며 전송 계층의 TLS와 같은 의미가 아닙니다. flow는 서버에서 해당 흐름 제어 방식을 명시적으로 활성화한 경우에만 입력하고, 다른 노드 설정을 근거 없이 복사하지 마세요.
streamSettings는 프로토콜 데이터를 어떤 방식으로 전달할지 설명합니다. network는 서버 설정과 일치해야 하며, security는 TLS 또는 다른 보안 계층의 사용 여부를 결정합니다. serverName은 인증서 이름과 핸드셰이크 대상에 사용됩니다. 원격 주소, 핸드셰이크 이름과 실제 인증서 이름은 서로 다른 역할을 할 수 있으므로 자주 같다는 이유만으로 언제나 서로 바꿀 수 있다고 생각하면 안 됩니다. allowInsecure는 인증서 검증 동작을 제어하며 정상적인 설정에서는 검증을 유지해야 합니다. 인증서 이름이 일치하지 않으면 검증을 끄기보다 노드 매개변수, 시스템 시간, 서버 이름과 중간 네트워크를 확인하세요.
REALITY 연결 매개변수 대응 방식
{
"streamSettings": {
"network": "tcp",
"security": "reality",
"realitySettings": {
"serverName": "www.example.com",
"fingerprint": "chrome",
"publicKey": "example-public-key",
"shortId": "0123456789abcdef",
"spiderX": "/"
}
}
}
REALITY 설정은 보통 realitySettings에 있습니다. serverName, publicKey, shortId와 서버 설정 사이에는 엄격한 대응 관계가 있으며 하나라도 잘못 입력하면 핸드셰이크 단계에서 실패할 수 있습니다. fingerprint는 클라이언트 핸드셰이크 지문 옵션이므로 커널과 서버 구성이 지원하는 값을 사용해야 합니다. 예제의 공개 키는 필드 형식을 설명하기 위한 값일 뿐 실제 연결에는 사용할 수 없습니다.
REALITY 노드에 연결할 수 없다면 먼저 네트워크 접근 불가, 시간 오류, 매개변수 불일치와 라우팅 오분류를 구분하세요. 원격 주소와 포트에 접근할 수 있는지 확인한 다음 서버 이름, 공개 키, 짧은 식별자와 흐름 제어 매개변수를 대조하고, 마지막으로 대상 서버 주소가 잘못된 직결 또는 차단 규칙의 영향을 받는지 확인합니다. 프로토콜 이름만 보고 설정을 반복해서 바꾸는 것으로는 실제 문제를 찾기 어렵습니다.
직결, 차단과 DNS 전용 아웃바운드
[
{
"tag": "direct",
"protocol": "freedom",
"settings": {
"domainStrategy": "UseIP"
}
},
{
"tag": "block",
"protocol": "blackhole",
"settings": {
"response": {
"type": "none"
}
}
}
]
freedom은 현재 기기가 대상에 직접 연결하도록 합니다. domainStrategy는 아웃바운드가 도메인에 연결할 때 도메인을 해석할지와 그 방식을 결정하지만, 최상위 routing.domainStrategy와는 다른 제어 지점입니다. 전자는 직결 아웃바운드가 연결을 수립하는 단계에, 후자는 라우팅 대조 단계에 적용됩니다. blackhole은 일치한 연결을 종료하며 명확한 차단 규칙에 적합합니다. 차단되면 애플리케이션에는 보통 연결 실패만 표시되므로 라우팅 로그에서 block에 일치했는지 확인해야 합니다.
구독 노드와 수동 설정의 경계
v2rayN, v2rayNG와 v2flyNG는 구독 내용을 각자 지원하는 노드 모델로 변환한 뒤 하위 아웃바운드 설정을 생성합니다. 구독을 업데이트하면 노드 프로토콜 매개변수가 덮어써질 수 있으므로 자동 생성 파일에서 서버 필드를 장기적으로 직접 관리하는 것은 적합하지 않습니다. 로컬 라우팅, DNS 또는 인바운드를 조정해야 한다면 클라이언트가 제공하는 사용자 지정 설정, 라우팅 프리셋 또는 덮어쓰기 기능을 우선 사용하세요. 클라이언트가 특정 필드를 지원하지 않는다면 사용하는 커널 계열이 해당 필드를 인식하는지 먼저 확인한 뒤 전체 사용자 지정 설정을 사용할지 결정하세요.
데스크톱 환경에서 v2rayN을 우선 추천하는 이유는 노드 관리, 라우팅 설정과 실행 로그를 한곳에서 제공해 이 장의 필드와 대조하기 쉽기 때문입니다. 설치 패키지는 설치 패키지 페이지에서 플랫폼에 맞게 선택하세요. Android에서는 커널 요구 사항에 따라 v2rayNG와 v2flyNG 중에서 선택할 수 있습니다. 두 클라이언트는 설정 생성 방식의 세부 사항이 다를 수 있으므로 동일한 구독을 가져온 뒤에도 각자의 실행 로그를 기준으로 확인해야 합니다.
routing 라우팅: 규칙 대조와 트래픽 분할 순서
라우팅은 프로토콜 자체를 바꾸지 않고 요청 특성에 따라 트래픽을 이미 정의된 아웃바운드로 전달합니다.
규칙은 위에서 아래로 대조됩니다
routing.rules는 순서가 있는 배열입니다. 요청이 라우터에 도착하면 규칙을 보통 위에서 아래로 확인하며, 먼저 일치한 규칙이 대상 아웃바운드를 결정합니다. 따라서 범위가 좁고 의도가 명확한 규칙을 앞에, 범위가 넓은 규칙을 뒤에 배치해야 합니다. 예를 들어 로컬 네트워크 직결은 광범위한 프록시 규칙보다 앞에, 특정 도메인 차단은 일반 도메인 프록시 규칙보다 앞에 둡니다. 모든 포트 프록시 규칙을 맨 앞에 두면 뒤의 로컬 네트워크 직결 규칙은 적용될 기회를 잃습니다.
규칙의 type은 보통 field를 사용하고, 이어서 domain, ip, port, network, protocol, inboundTag 등의 조건으로 대상을 설명합니다. 하나의 규칙에 여러 조건이 포함되면 조건을 조합해 모두 충족해야 일치하며, 동일 필드의 여러 값은 보통 그중 하나만 일치해도 됩니다. 서로 관계없는 조건을 하나의 규칙에 넣으면 예상보다 범위가 좁아질 수 있습니다. 더 명확한 방법은 여러 규칙으로 나누고 각 규칙에 하나의 목적만 부여하는 것입니다.
자주 사용하는 기본 트래픽 분할 구조
{
"routing": {
"domainStrategy": "IPIfNonMatch",
"domainMatcher": "hybrid",
"rules": [
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "direct"
},
{
"type": "field",
"domain": ["geosite:private"],
"outboundTag": "direct"
},
{
"type": "field",
"protocol": ["bittorrent"],
"outboundTag": "direct"
},
{
"type": "field",
"domain": [
"domain:example.net",
"full:status.example.org"
],
"outboundTag": "proxy"
}
]
}
}
geoip:private는 일반적인 사설 주소 범위와 일치하므로 라우터, 프린터와 로컬 네트워크 서비스에 접속할 때 원격으로 우회하지 않게 하는 데 적합합니다. geosite:private는 해당 사설 도메인 모음을 처리합니다. GeoIP와 GeoSite는 서로 다른 데이터 출처입니다. 전자는 IP 범위로 분류하고 후자는 도메인 집합으로 분류합니다. 데이터 파일이 오래되면 분류 결과가 최신 상태와 어긋날 수 있으므로 업데이트 방법은 GeoIP 및 GeoSite 데이터 파일 업데이트 방법을 참고하세요.
domain:은 지정한 도메인과 하위 도메인을 일치시키고, full:은 완전한 호스트 이름만 일치시키며, keyword:는 키워드로 일치시킵니다. 정확한 서비스에는 full:을 우선 사용하고, 사이트와 하위 도메인을 함께 지정할 때는 domain:을 사용할 수 있습니다. 키워드는 범위가 넓어 이름이 비슷하지만 관계없는 도메인까지 잘못 일치할 수 있으므로 로그로 확인한 명확한 범위에 적합합니다. 규칙의 일반 문자열은 커널 설정 문맥에 따라 기본 해석이 달라질 수 있으므로 수동 설정에서는 접두사를 사용해 모호함을 줄이세요.
domainStrategy가 도메인 및 IP 규칙에 미치는 영향
AsIs는 라우팅 단계에서 원래 대상을 기준으로 처리하려고 합니다. 요청이 도메인으로 들어오면 도메인 규칙이 대조에 참여할 수 있지만, IP만 포함된 요청에서는 도메인 이름을 임의로 복원할 수 없어 도메인 규칙이 보통 적용되지 않습니다. IPIfNonMatch는 도메인 규칙이 일치하지 않을 때 도메인을 해석한 뒤 IP 규칙을 시도합니다. IPOnDemand는 IP가 필요한 규칙을 만났을 때 더 일찍 해석을 시작할 수 있습니다. 전략에 따라 DNS 조회 시점, 라우팅 일치 여부와 연결 시간이 달라지므로 이름만 보고 선택해서는 안 됩니다.
도메인 집합을 우선하고 IP 지리 규칙을 보완으로 사용하려면 IPIfNonMatch가 보통 이해하기 쉽습니다. 먼저 도메인을 확인하고 일치하지 않을 때 해석한 뒤 IP를 확인합니다. IP 규칙이 많고 주소를 일찍 확보해야 한다면 IPOnDemand를 검토할 수 있습니다. 라우터가 규칙 대조를 위해 능동적으로 해석하는 것을 원하지 않는다면 AsIs를 사용하되, 인바운드 스니핑이나 애플리케이션 요청에 충분한 대상 정보가 제공되는지 확인하세요.
포트, 네트워크와 인바운드 태그별 트래픽 분할
{
"type": "field",
"inboundTag": ["socks-in"],
"network": "tcp",
"port": "443",
"outboundTag": "proxy"
}
포트는 단일 값이나 범위로 작성할 수 있으며 예시는 80,443, 10000-20000과 같습니다. 포트는 대상 서비스의 포트만 설명할 뿐 특정 웹사이트나 애플리케이션을 의미하지 않습니다. 많은 서비스가 443 포트를 공유하므로 포트만으로 분할하면 범위가 지나치게 넓어질 수 있습니다. network는 TCP와 UDP를 구분하고, inboundTag는 요청이 들어온 로컬 인바운드에 따라 분할합니다. 소프트웨어별로 독립 인바운드를 만든 뒤 인바운드 태그를 사용하는 방식이 프로세스 이름을 추측하는 것보다 범용적입니다.
규칙 불일치와 잘못된 일치
어떤 규칙도 일치하지 않으면 요청은 기본 아웃바운드로 이동합니다. 기본 동작은 설정 구조와 커널 구현을 통해 확인해야 하며, 반드시 직결이거나 반드시 프록시라고 가정하지 마세요. 문제를 해결할 때는 먼저 적절한 수준의 실행 로그를 켜고 요청 대상, 인바운드 태그와 최종 아웃바운드 태그를 기록합니다. 규칙이 일치하지 않으면 요청에 도메인과 IP 중 무엇이 포함되었는지, 도메인 스니핑이 작동하는지, Geo 데이터가 존재하는지와 규칙 접두사가 올바른지 확인하세요. 잘못 일치하면 배열의 처음부터 내려오며 해당 대상을 포괄할 수 있는 첫 번째 규칙을 찾습니다.
라우팅을 조정한 뒤에는 명확한 대상 몇 가지로 각각 확인하세요. 로컬 네트워크 주소 하나, 직결할 도메인 하나, 프록시로 보낼 도메인 하나와 차단 대상 하나를 사용합니다. 대부분의 웹페이지가 열린다는 사실만으로 트래픽 분할이 올바르다고 판단하지 마세요. 기본 출구가 규칙 오류를 가릴 수 있습니다. 노드 속도 측정도 특정 테스트 요청의 연결 결과만 보여줄 뿐 하나씩 검증하는 과정을 대신할 수 없습니다. 시스템 프록시와 터미널의 경로가 다른 문제는 브라우저와 명령줄 터미널을 나누어 문제 해결하기를 참고하세요.
dns 설정: 해석 경로, 서버 선택과 라우팅 연계
DNS는 단순한 서버 주소 목록이 아니라 도메인 규칙, IP 규칙과 최종 아웃바운드 연결에도 관여합니다.
시스템 해석과 내장 해석을 먼저 구분하세요
애플리케이션이 연결을 시작할 때 도메인은 애플리케이션이나 운영 체제가 먼저 해석할 수도 있고, 도메인 형태로 로컬 프록시에 전달될 수도 있습니다. 전자의 경우 인바운드에서 이미 IP를 확인할 수 있으며, 후자의 경우에만 V2Ray의 내장 DNS와 라우팅 전략이 해석에 직접 참여할 수 있습니다. TUN 모드를 활성화하면 더 많은 시스템 트래픽이 클라이언트 처리 체인으로 들어올 수 있지만, 실제 요청이 내장 DNS에서 처리되는지는 클라이언트가 생성한 인바운드, DNS 가로채기와 라우팅 설정에 따라 달라집니다.
따라서 dns.servers를 설정했다고 해서 기기의 모든 이름 조회가 자동으로 이를 사용하는 것은 아닙니다. 브라우저 자체의 보안 DNS, 애플리케이션 내장 해석기, 시스템 캐시와 터미널 도구가 각각 독립적인 경로를 만들 수 있습니다. 해석 이상을 확인할 때는 먼저 세 가지 질문에 답해야 합니다. 누가 조회를 시작했는가, 어느 서버로 보냈는가, 반환된 주소가 최종적으로 라우팅 규칙에 사용되었는가입니다. 경로를 확인하지 않고 DNS 서버만 바꾸면 설정을 변경해도 현상이 그대로일 수 있습니다.
기본 DNS 객체 예제
{
"dns": {
"hosts": {
"domain:internal.example": "192.0.2.10"
},
"servers": [
{
"address": "1.1.1.1",
"domains": ["geosite:geolocation-!cn"],
"skipFallback": true
},
{
"address": "223.5.5.5",
"domains": ["geosite:cn"],
"expectIPs": ["geoip:cn"]
},
"localhost"
],
"queryStrategy": "UseIP"
}
}
hosts는 정적 도메인 매핑을 제공하므로 통제된 테스트와 고정된 내부 주소에 적합하지만 계속 변하는 공용 해석을 대신할 수는 없습니다. 예시 주소는 구조 설명만을 위한 것입니다. servers에는 단순 주소를 넣을 수도 있고 객체 형식으로 도메인 범위, 예상 IP 범위와 폴백 제어를 추가할 수도 있습니다. 조회 대상은 목록 순서와 일치 조건이 함께 결정합니다. 여러 서버를 설정한다고 모든 서버가 단순히 동시에 경쟁하는 것은 아니며, 실제 동작은 도메인 조건, 폴백 로직과 커널 구현에 따라 달라집니다.
domains는 특정 서버가 우선 처리할 도메인 집합을 지정하며 문법은 라우팅 도메인 규칙과 비슷합니다. expectIPs는 반환 주소가 예상한 분류에 맞는지 확인하는 기능이며 범용 주소 필터를 대신하지 않습니다. 반환 결과가 조건을 충족하지 않으면 해석기가 다른 서버를 시도할 수 있습니다. 데이터 파일이 없거나 오래되면 Geo 분류에 기반한 예상 판단도 정확하지 않습니다. skipFallback은 해당 서버가 폴백에 참여할지에 영향을 주므로 모든 항목에 일괄 활성화하기보다 현재 서버 그룹을 이해한 뒤 설정하세요.
queryStrategy와 주소 체계 선택
queryStrategy는 조회에 사용할 주소 체계 전략을 제어합니다. 일반적으로 사용 가능한 주소를 모두 고려하거나 IPv4만 또는 IPv6만 사용하는 의미를 가지며, 실제로 사용할 수 있는 값은 현재 커널 지원 범위와 일치해야 합니다. 주소 체계 선택은 기기의 네트워크 환경에 맞아야 합니다. 로컬 네트워크에 사용 가능한 IPv6 경로가 없는데 IPv6만 조회하도록 강제하면 도메인 해석은 성공해도 이후 연결은 실패할 수 있습니다. 반대로 특정 주소 체계에서만 네트워크가 연결되는 경우에도 기본 결과에만 의존해서는 안 됩니다.
듀얼 스택 환경의 실패는 여러 주소가 해석되었지만 애플리케이션이나 아웃바운드가 연결할 수 없는 주소를 먼저 시도해서 발생할 수도 있습니다. 이때는 DNS 반환 결과, 아웃바운드 선택과 연결 로그를 함께 확인해야 합니다. 모든 타임아웃을 DNS 탓으로 돌리지 마세요. 해석 로그에 주소가 표시된 뒤 연결 단계에서 타임아웃이 발생했다면 문제는 이미 아웃바운드 네트워크나 원격 서비스 단계에 들어간 것입니다. 반대로 도메인 조회 실패, 사용 가능한 레코드 없음 또는 모든 예상 조건 불충족이 로그에 나타날 때 DNS 구간을 집중적으로 점검하세요.
DNS 조회는 어느 아웃바운드로 나가는가
DNS 서버 자체도 연결 대상이므로 라우팅 규칙에 따라 직결 또는 프록시 아웃바운드로 전달될 수 있습니다. DNS 서버를 도메인으로 표시하면 'DNS 서버 도메인을 해석하기 위한 선행 해석' 문제가 생길 수도 있습니다. 순환 의존성을 줄이기 위해 기본 DNS 서버 주소에는 직접 연결할 수 있는 IP를 사용하거나 명확한 전용 아웃바운드와 라우팅 규칙으로 처리하는 경우가 많습니다. HTTPS 기반 해석 엔드포인트를 사용할 때는 엔드포인트 도메인, 인증서 이름과 자체 해석 경로도 고려해야 합니다.
DNS 조회에 독립적인 아웃바운드 태그를 지정한 뒤 프로토콜 조건이나 인바운드 태그로 라우팅할 수 있습니다. 다만 경로는 명확하게 유지해야 합니다. 요청이 내장 DNS로 들어오고, DNS 조회가 지정된 아웃바운드를 통해 서버로 전달된 뒤 결과가 라우터로 돌아와 대조에 사용되고, 최종 업무 트래픽은 업무용 아웃바운드로 나가야 합니다. DNS 조회를 동일한 DNS 결과에 의존하는 프록시 아웃바운드로 잘못 보내면 시작 단계에서 순환 대기가 발생할 수 있습니다. 클라이언트 프리셋은 보통 기본 의존성을 처리하므로 수동으로 덮어쓸 때 익숙하지 않은 DNS 라우팅을 삭제하지 마세요.
캐시, Fake DNS와 TUN 환경
시스템 캐시, 애플리케이션 캐시와 내장 해석 캐시가 동시에 존재할 수 있습니다. DNS를 변경한 직후 같은 도메인에 반복 접속해도 기존 결과가 즉시 사라지지 않을 수 있습니다. 확인할 때는 대상 애플리케이션과 클라이언트를 재시작하거나 아직 조회하지 않은 새 도메인으로 전체 과정을 관찰하세요. TUN 모드에서는 Fake DNS가 도메인을 예약 주소에 매핑한 뒤 트래픽이 들어올 때 도메인으로 되돌려 도메인 라우팅을 지원할 수 있습니다. 이러한 예약 주소를 실제 원격 IP로 취급하거나 GeoIP 분류 판단에 직접 사용해서는 안 됩니다.
Fake DNS는 시스템에 IP 트래픽만 전달되어 도메인 정보가 사라지는 문제를 해결하는 데 적합하지만, DNS 인계, 매핑 테이블과 TUN 인바운드가 일관되게 구성되어야 합니다. 애플리케이션이 주소를 받았지만 연결하지 못한다면 동일한 실행 인스턴스가 매핑을 인식하는지, 요청이 TUN을 우회했는지와 예약 주소가 다른 규칙에 의해 먼저 직결되었는지 확인하세요. TUN의 시스템 트래픽 인계 원리와 클라이언트 인바운드는 TUN 모드의 전체 트래픽 인계 원리 자세히 알아보기를 참고하세요.
policy 정책: 세션 타임아웃, 통계와 시스템 제어
policy는 노드를 선택하지 않고 연결 수명과 통계 정보에 대한 실행 범위를 설정합니다.
levels와 사용자 등급 참조
policy.levels는 사용자 등급을 키로 사용해 해당 등급의 연결에 정책을 설정합니다. 일부 인바운드나 아웃바운드 프로토콜의 사용자 객체에는 level을 지정할 수 있으며, 실행 시 같은 이름의 정책을 조회합니다. 등급은 속도 점수나 권한 순위를 자동으로 정하는 값이 아니라 정책 집합을 참조하기 위한 숫자 키일 뿐입니다. 사용자에게 등급을 명시하지 않으면 보통 기본 등급을 사용하지만, 정확한 기본 동작은 현재 설정과 커널 구현을 함께 확인해야 합니다.
동일한 등급으로 핸드셰이크 시간, 연결 유휴 시간과 업로드·다운로드 통계 등을 일괄 제어할 수 있습니다. '완전해 보이게' 하려고 참조되지 않는 등급을 대량으로 만들지 마세요. 클라이언트가 생성한 단일 사용자 아웃바운드에서는 통계가 활성화되어 있는지 또는 타임아웃이 변경되었는지 정도만 기본 등급에서 확인하면 충분한 경우가 많습니다. 서버의 다중 사용자 환경에서는 등급을 더 자주 구분하지만, 이 페이지는 클라이언트 설정 읽기에 초점을 두므로 정상 연결을 의도치 않게 짧게 끊는 정책이 없는지에 더 주의해야 합니다.
정책 객체 예제
{
"policy": {
"levels": {
"0": {
"handshake": 4,
"connIdle": 300,
"uplinkOnly": 2,
"downlinkOnly": 5,
"statsUserUplink": false,
"statsUserDownlink": false,
"bufferSize": 4
}
},
"system": {
"statsInboundUplink": true,
"statsInboundDownlink": true,
"statsOutboundUplink": true,
"statsOutboundDownlink": true
}
},
"stats": {}
}
handshake는 연결 수립 단계에서 기다릴 수 있는 시간을 제한합니다. 너무 짧게 설정하면 네트워크 변동, 이름 해석 또는 원격 핸드셰이크가 조금만 느려도 연결이 조기에 종료될 수 있으며, 너무 길면 접근할 수 없는 연결의 실패를 확인하기까지 오래 걸립니다. connIdle은 데이터 전송이 없을 때 연결을 유지할 수 있는 시간을 나타냅니다. 장시간 연결을 사용하는 애플리케이션, 푸시 메시지, 원격 터미널과 지속 전송 도구는 유휴 시간에 민감하므로 웹 브라우징 경험만으로 조정해서는 안 됩니다.
uplinkOnly와 downlinkOnly는 한 방향의 데이터만 남은 뒤 연결을 유지할 시간을 처리합니다. 이는 연결 수명 관리용이며 대역폭 제한을 의미하지 않습니다. bufferSize는 연결별 데이터 버퍼링 방식을 결정합니다. 무작정 크게 하면 메모리 사용량이 늘고, 지나치게 줄이면 처리량에 영향을 줄 수 있습니다. 대부분의 클라이언트 사용자는 커널이나 클라이언트가 제공하는 기본값을 유지하고, 로그와 재현 가능한 테스트에서 문제가 연결 수명과 관련된 것으로 확인될 때만 조정해야 합니다.
통계 스위치는 쌍으로 이해해야 합니다
statsUserUplink와 statsUserDownlink은 사용자 단위 통계를 제어하고, policy.system의 항목은 인바운드 및 아웃바운드 단위 통계를 제어합니다. "stats": {}만 작성한다고 모든 지표가 자동 수집되는 것은 아니며 해당 policy 스위치도 활성화해야 합니다. 반대로 통계를 켜도 이를 읽는 인터페이스나 API가 없다면 불필요한 기록 작업만 늘어납니다. 데스크톱 클라이언트가 트래픽 통계를 표시하는지는 커널을 어떻게 시작하고 통계 인터페이스를 읽는지에 따라 달라집니다.
통계 값은 현재 실행 인스턴스의 트래픽 방향을 관찰하는 데 적합하지만 구독 잔여량, 결제 기록이나 원격 서버 트래픽의 공식 출처로 사용해서는 안 됩니다. 클라이언트 재시작, 설정 재로드와 커널 전환으로 로컬 통계가 다시 시작될 수 있습니다. '인터페이스에 트래픽 숫자가 표시되지 않는' 문제를 해결할 때는 먼저 클라이언트가 하위 통계를 읽도록 설계되었는지 확인하고, 그다음 stats 객체와 system 스위치를 확인하세요. 프로토콜 매개변수부터 바로 수정하지 마세요.
policy와 타임아웃 오류의 관계
로그의 timeout은 이름 해석, TCP 연결, TLS 또는 REALITY 핸드셰이크, 프록시 프로토콜 핸드셰이크, 업무 데이터 읽기·쓰기 등 서로 다른 단계에서 발생할 수 있습니다. policy는 이 중 일부 세션 타임아웃만 설명합니다. 연결이 일정하고 매우 짧은 시간 뒤 끊긴다면 handshake와 connIdle을 확인하세요. 로그에 원격 주소 연결 타임아웃이 명확히 표시되면 먼저 네트워크와 아웃바운드를 점검하세요. 특정 애플리케이션의 장시간 연결만 주기적으로 끊긴다면 유휴 주기와 정책 값을 비교하세요.
타임아웃을 변경하기 전에 오류가 발생한 단계와 시간 패턴을 기록하세요. 모든 값을 무작정 크게 하면 오류 피드백이 늦어지고 실패한 연결이 리소스를 오래 점유합니다. 반대로 값을 일괄적으로 줄이면 느린 네트워크와 장시간 연결에 문제가 생깁니다. 기본 정책을 기준선으로 유지하고 재현 가능한 상황에서 한 항목만 조정한 뒤, 클라이언트를 재시작해 같은 대상을 다시 테스트하는 것이 바람직합니다.
시스템 정책과 설정 이식성
클라이언트와 커널 계열에 따라 지원하는 정책 필드나 기본값이 다를 수 있습니다. v2rayN이 사용하는 실행 커널, v2rayNG에서 흔히 사용하는 Xray 커널과 v2flyNG에 해당하는 v2fly 커널은 공통 필드 외에도 동작 차이가 있을 수 있습니다. 전체 설정을 다른 클라이언트로 복사하기 전에 대상 클라이언트가 완전한 사용자 지정 설정을 허용하는지, 시작할 때 policy를 다시 작성하는지 확인해야 합니다.
설정 이식성의 핵심은 필드를 적게 만드는 것이 아니라 표준 구조, 커널 확장과 클라이언트 생성 내용을 명확히 구분하는 데 있습니다. 인바운드 포트, 로그 경로와 TUN 매개변수는 플랫폼 환경에 종속되는 경우가 많고, 서버 프로토콜 매개변수는 대체로 이전할 수 있습니다. 클라이언트 인터페이스 상태와 실행 설정은 반드시 일대일로 대응하지 않습니다. 플랫폼을 옮길 때는 먼저 구독을 가져와 실행 가능한 기준선을 만든 다음 라우팅, DNS와 정책을 하나씩 이전하는 편이 전체 파일을 덮어쓰는 것보다 비호환 필드를 찾기 쉽습니다.
설정 로드, 클라이언트 덮어쓰기와 검증 과정
문법 검증, 구조 검증과 실제 연결 검증을 분리하면 문제 범위를 빠르게 좁힐 수 있습니다.
1단계: JSON을 해석할 수 있는지 확인
문법 검증은 설정이 올바른 JSON인지 여부만 확인합니다. 큰따옴표, 쉼표, 대괄호와 중괄호가 쌍을 이루는지, 숫자·불리언·문자열이 올바른 형식을 사용하는지 중점적으로 확인하세요. JSON은 주석과 객체 끝의 불필요한 쉼표를 허용하지 않습니다. 웹페이지, 대화 기록이나 문서에서 설정을 복사할 때는 먼저 순수 텍스트 UTF-8 파일로 저장해 굽은 따옴표와 보이지 않는 제어 문자를 피하세요.
문법 오류에는 보통 행과 열 번호가 표시되지만 실제 오류는 이전 행에 있을 수 있습니다. 예를 들어 새 속성의 시작 부분에서 파서 오류가 발생하면 흔한 원인은 이전 항목 끝에 쉼표가 빠진 것입니다. 파일 끝 오류가 나타나면 먼저 객체나 배열이 닫히지 않았는지 확인하세요. 편집기의 괄호 짝 확인 기능은 유용하지만 데이터 형식과 필드 계층 검사를 대신할 수는 없습니다.
2단계: 태그와 필드가 올바른 위치에 있는지 확인
올바른 JSON이라고 해서 올바른 V2Ray 설정인 것은 아닙니다. 2단계 검증에서는 최상위 필드 이름, 프로토콜 전용 settings, 전송 계층 streamSettings와 라우팅 참조를 확인해야 합니다. 흔한 구조 오류로는 tlsSettings를 아웃바운드 루트에 배치하는 것, 단일 아웃바운드 객체를 배열이 아닌 객체로 작성하는 것, 규칙에서 존재하지 않는 outboundTag를 참조하는 것과 문자열 배열을 단일 문자열로 작성하는 것이 있습니다.
알 수 없는 필드를 처리하는 방식은 커널에 따라 다를 수 있습니다. 어떤 커널은 로드를 거부하고 어떤 커널은 필드를 무시할 수 있습니다. 무시는 즉시 오류가 발생하는 것보다 해결하기 어렵습니다. 설정이 시작된 것처럼 보이지만 예상한 기능이 작동하지 않기 때문입니다. 로그에 unknown field, failed to parse, failed to build 또는 태그를 찾을 수 없다는 메시지가 나타나면 네트워크 연결성 테스트를 계속하기보다 해당 객체 계층으로 돌아가 확인하세요.
3단계: 클라이언트가 실제로 어떤 설정을 로드했는지 확인
v2rayN, v2rayNG와 v2flyNG는 인터페이스 설정에 따라 시작할 때 실행 설정을 생성할 수 있습니다. 사용자가 편집한 파일이 커널이 최종적으로 읽는 파일과 다를 수 있으며, 특히 구독 노드, 라우팅 프리셋, 시스템 프록시와 TUN 모드를 함께 활성화한 경우 그렇습니다. 검증하기 전에 클라이언트 로그에서 설정 생성과 커널 시작이 성공했는지 확인하고, 클라이언트가 제공하는 내보내기·미리 보기 또는 실행 디렉터리 기능을 확인하세요.
구독 업데이트는 보통 노드 매개변수를 교체하지만 로컬 라우팅까지 교체하지는 않을 수 있습니다. 완전한 사용자 지정 설정은 일부 인터페이스 설정을 우회할 수도 있습니다. 현재 방식이 '구독 노드와 클라이언트 템플릿'인지 '완전한 사용자 지정 설정'인지 명확히 구분해야 합니다. 두 방식을 섞으면 인터페이스에서 포트를 바꿨지만 실행 파일은 변하지 않거나 수동 라우팅이 프리셋에 의해 다시 생성되는 문제가 자주 발생합니다. 구독 새로 고침 실패에 대한 별도 해결 방법은 구독 업데이트 실패와 자동 업데이트 설정을 참고하세요.
단계별로 연결 기준선 만들기
- 시작 확인: 커널 시작이 완료되었고 로컬 SOCKS 또는 HTTP 포트가 수신 중이며 포트 충돌과 설정 해석 오류가 없는지 확인합니다.
- 로컬 인바운드 확인: 프록시를 명확히 지원하는 애플리케이션 하나로 로컬 포트에 연결하고 접근 로그에 해당 인바운드 태그가 표시되는지 확인합니다.
- 단일 아웃바운드 확인: 잠시 단순한 라우팅을 사용해 대상 요청이 프록시 아웃바운드를 통해 연결되는지 확인하고 복잡한 규칙의 간섭을 피합니다.
- DNS와 라우팅 복원: 로컬 네트워크, 도메인, GeoIP와 차단 규칙을 그룹별로 추가하고 매번 요청이 최종적으로 사용한 아웃바운드를 기록합니다.
- 시스템 인계 복원: 마지막에 시스템 프록시나 TUN을 활성화해 더 많은 애플리케이션을 처리 체인에 넣고 브라우저와 터미널을 각각 확인합니다.
이 순서는 문제를 시작, 로컬 인바운드, 원격 아웃바운드, 트래픽 분할과 시스템 인계의 다섯 계층으로 나눕니다. 첫 단계가 끝나지 않았다면 원격 프로토콜을 분석할 필요가 없습니다. 로컬 인바운드에 요청이 들어오지 않으면 애플리케이션 프록시 설정을 확인해야 합니다. 단순한 아웃바운드는 작동하지만 라우팅을 복원한 뒤 실패한다면 문제는 DNS나 규칙에 집중됩니다. 수동 프록시는 작동하지만 시스템 인계 후 이상이 발생한다면 시스템 프록시 상태, TUN 권한과 라우팅 충돌을 중점적으로 확인하세요.
로그 수준과 읽기 쉬운 정보
log.loglevel은 문제 해결 중 더 많은 세부 정보를 포함하는 수준으로 조정할 수 있습니다. 기록이 상세할수록 인바운드, 라우팅과 아웃바운드를 관찰하기 쉽지만 출력량도 늘어납니다. 정상 사용에서는 높은 상세도를 계속 유지할 필요가 없습니다. 문제 해결 로그는 한 번의 명확한 테스트를 중심으로 커널 시작부터 대상 요청의 성공 또는 실패까지 잘라 기록하고, 많은 과거 기록 속에서 서로 다른 설정을 혼동하지 않도록 하세요.
{
"log": {
"access": "",
"error": "",
"loglevel": "info",
"dnsLog": true
}
}
로그 설정의 구체적인 경로와 출력 방식은 클라이언트가 인계할 수 있습니다. 빈 문자열이 콘솔 출력을 뜻하는지도 현재 커널과 클라이언트 동작을 함께 확인해야 합니다. dnsLog는 내장 해석 과정을 관찰하는 데 도움이 되지만 내장 DNS를 거친 조회만 표시됩니다. 전체 로그 읽기 방법은 V2Ray 실행 로그의 흔한 오류 의미와 위치 찾기를 참고하세요.
변경 기록이 반복적인 시행착오보다 효과적입니다
매 테스트마다 하나의 필드 그룹만 변경하고 변경 전 값, 변경 후 값, 테스트 대상과 로그 결과를 기록하세요. 예를 들어 domainStrategy를 AsIs에서 IPIfNonMatch로 바꿨다면 IP 규칙이 필요한 도메인만 확인합니다. 노드, DNS 서버와 TUN 모드를 동시에 변경하지 마세요. 결과가 나빠져도 정확히 되돌릴 수 있고, 개선되었을 때 어떤 항목이 효과를 냈는지 알 수 있습니다.
클라이언트를 업그레이드하거나 커널을 전환한 뒤에는 기존 설정이 모두 호환된다고 가정하지 말고 최소 검증 과정을 다시 수행해야 합니다. 필드 지원 여부를 판단하기 위해 고정된 버전 번호를 지어내거나 의존하지 마세요. 현재 클라이언트가 선택한 커널 유형, 시작 로그와 설정 오류를 확인해야 합니다. 클라이언트를 다시 설치해야 한다면 설치 패키지 페이지에서 Windows, macOS, Android 또는 Linux에 해당하는 항목을 선택하세요.
흔한 오류의 위치 찾기와 장기 유지 관리 방법
전체 설정 구간의 스위치를 무작위로 바꾸지 말고 오류가 발생한 계층을 기준으로 처리하세요.
시작 실패: 먼저 문법, 포트와 참조를 확인
커널이 시작된 직후 종료된다면 보통 세 가지 문제를 먼저 확인해야 합니다. 첫째는 쉼표 누락, 괄호 불일치와 잘못된 데이터 형식을 포함한 JSON 문법 오류입니다. 둘째는 다른 프로세스가 수신 포트를 사용 중이거나 로그 디렉터리에 쓸 수 없거나 TUN 장치를 만들 수 없는 로컬 리소스 충돌입니다. 셋째는 라우팅 대상 태그가 존재하지 않거나 프로토콜 설정에 필수 필드가 빠졌거나 현재 커널이 특정 확장 항목을 인식하지 못하는 설정 객체 참조 오류입니다.
처리 순서는 로그와 일치해야 합니다. 해석 오류가 읽기 단계에서 발생했다면 먼저 파일을 수정하세요. 주소 사용 중 오류가 수신 단계에서 발생했다면 중복 클라이언트와 로컬 포트를 확인하세요. 아웃바운드를 찾지 못했다면 라우팅 구성 단계의 문제이므로 태그를 대조하세요. 커널이 명확히 실행 상태에 들어간 뒤에만 원격 연결 문제를 확인해야 합니다. 클라이언트 인터페이스에 '연결되지 않음'이 표시된다는 사실만으로는 어느 계층의 문제인지 판단할 수 없으므로 마지막 오류 로그를 확인해야 합니다.
로컬 포트는 있지만 애플리케이션에 트래픽이 없습니다
먼저 애플리케이션에 올바른 프록시 유형과 포트가 입력되었는지 확인하세요. HTTP 프록시와 SOCKS 프록시는 서로 다른 인바운드이며 시스템 프록시가 터미널과 백그라운드 서비스에서 자동으로 사용된다는 보장도 없습니다. 그다음 애플리케이션이 로컬 주소를 우회하는지, 자체 해석이나 프록시 설정을 사용하는지, 요청이 실제로 access 로그에 도착했는지 확인합니다. 로그에 이번 요청이 전혀 없다면 문제는 애플리케이션과 인바운드 사이에 있습니다. 로그에 인바운드 기록이 나타난 뒤에야 트래픽이 V2Ray에 들어온 것입니다.
일부 애플리케이션만 작동하지 않는다면 해당 애플리케이션에 명확한 SOCKS 또는 HTTP 인바운드를 설정해 비교하세요. 수동 인바운드는 작동하지만 시스템 프록시가 작동하지 않는다면 운영 체제의 프록시 상태와 제외 목록을 확인하세요. 시스템 프록시에서 브라우저는 작동하지만 터미널이 작동하지 않는다면 터미널 도구가 지원하는 프록시 옵션을 설정하세요. 한 애플리케이션이 시스템 프록시를 우회한다고 해서 원격 노드 프로토콜을 바꾸지는 마세요.
도메인은 실패하지만 IP는 작동함
이 현상에서는 먼저 해석 경로를 확인하세요. 애플리케이션이 도메인을 제출했는지 이미 해석된 IP를 제출했는지, 내장 DNS가 조회를 받았는지, 서버가 사용 가능한 주소를 반환했는지와 queryStrategy가 현재 네트워크에서 접근 가능한 주소 체계를 선택했는지 확인합니다. 도메인 규칙이 스니핑에 의존한다면 인바운드가 대상 이름을 식별할 수 있는지도 확인해야 합니다. Fake DNS를 사용한다면 예약 주소가 동일한 실행 인스턴스에서 복원되는지 확인하세요.
DNS가 이미 주소를 반환했지만 아웃바운드 단계에서 연결 타임아웃이 발생한다면 문제는 더 이상 '해석 결과가 없음'이 아니라 해당 주소의 연결 경로, 라우팅 또는 원격 서비스에 있습니다. 반환 주소가 expectIPs에서 예상과 맞지 않는 것으로 판정되면 Geo 데이터와 서버 그룹을 확인하세요. 방금 수정한 도메인만 계속 이전 주소를 사용한다면 애플리케이션, 시스템과 내장 캐시를 고려하고 관련 프로세스를 재시작하거나 새 도메인으로 테스트해 캐시 영향을 배제하세요.
규칙은 올바른 것처럼 보이지만 일치하지 않음
먼저 요청의 실제 형태를 확인하세요. 라우터가 완전한 도메인, 하위 도메인 또는 IP 중 무엇을 받았는지, 규칙이 full:, domain: 또는 키워드를 사용하는지, 앞에 더 넓은 규칙이 먼저 일치하지 않았는지와 Geo 데이터 파일을 사용할 수 있는지 확인합니다. 규칙 문구와 대상이 비슷해 보여도 일치 의미가 같다는 뜻은 아닙니다. full:example.com은 다른 하위 도메인과 일치하지 않으며 지나치게 넓은 키워드는 관계없는 여러 대상과 일치할 수 있습니다.
문제 규칙을 잠시 배열 앞쪽으로 옮기면 다른 규칙에 가려졌는지 판단하는 데 도움이 되지만, 테스트가 끝난 뒤에는 전체 순서를 다시 검토해야 합니다. 더 신뢰할 수 있는 방법은 라우팅 관련 로그를 활성화해 요청이 최종적으로 선택한 아웃바운드 태그를 기록하는 것입니다. 요청에 IP만 포함되어 있다면 도메인 규칙이 일치하지 않는 것이 자연스럽습니다. 이때는 같은 도메인 문자열을 계속 고치기보다 sniffing, 애플리케이션의 해석 방식 또는 적절한 IP 규칙 추가를 확인하세요.
노드 매개변수는 올바르지만 핸드셰이크 실패
먼저 서버 주소와 포트에 접근할 수 있는지 확인한 뒤 프로토콜 인증 매개변수, 전송 방식과 보안 계층을 대조하세요. TLS 환경에서는 서버 이름, 시스템 시간과 인증서 오류를 확인하고, REALITY 환경에서는 서버 이름, 공개 키, 짧은 식별자, 지문과 흐름 제어 매개변수를 확인하세요. 다른 프로토콜도 클라이언트 매개변수와 서버 설정을 항목별로 일치시켜야 합니다. 서로 다른 노드의 전송 필드를 섞어 하나의 설정을 만들지 마세요. 각 계층에 의존 관계가 있을 수 있습니다.
구독 업데이트 후 갑자기 실패한다면 이전 노드를 비교 대상으로 남겨 두고 업데이트로 주소, 포트, 전송 방식 또는 인증 매개변수가 바뀌었는지 확인하세요. 누락된 필드를 추측해 수동으로 입력하지 마세요. 모든 노드가 동시에 실패한다면 먼저 로컬 네트워크, 시스템 시간, 커널 시작과 라우팅을 확인해야 합니다. 하나의 노드만 실패할 때 해당 노드의 매개변수나 원격 상태에 문제가 있을 가능성이 더 큽니다.
설정이 점점 커진 뒤 정리하는 원칙
장기간 사용하면 설정에 작동하지 않는 태그, 중복 도메인 규칙, 서로 덮어쓰는 Geo 규칙과 더 이상 사용하지 않는 인바운드가 쌓이기 쉽습니다. 정리할 때는 먼저 현재 처리 체인을 그려 모든 인바운드 태그, 아웃바운드 태그, 각 규칙의 대상과 DNS 서버 용도를 나열하세요. 참조가 없는 것을 확인한 뒤 객체를 삭제합니다. 같은 대상과 같은 출구를 사용하는 규칙은 합칠 수 있지만, 행 수를 줄이려고 목적이 다른 조건을 설명하기 어려운 하나의 규칙에 넣지는 마세요.
태그에 안정적인 명명 규칙을 만들고 '특수 차단, 로컬 네트워크 직결, 특정 프록시, 기본 처리' 순서로 라우팅을 배열하는 것이 좋습니다. DNS 서버 객체는 적용 도메인 범위별로 그룹화하고 policy에는 실제 사용하는 등급만 남기세요. 설정 파일 자체가 주석을 지원하지 않는다면 별도의 유지 관리 기록에 각 태그의 용도, 마지막 검증 상황과 클라이언트 생성 방식을 적어 두세요. 나중에 필드를 보고도 조정하지 못하는 상황을 막을 수 있습니다.
클라이언트 업데이트와 설정 이전
클라이언트를 업데이트하기 전에 현재 실행 가능한 설정, 라우팅 프리셋과 구독 설정을 저장하세요. 업데이트 후에는 기존 노드로 최소 연결 테스트를 먼저 진행한 다음 고급 기능을 확인합니다. 클라이언트가 실행 커널을 바꿨다면 시작 로그의 알 수 없는 필드와 사용 중단 알림에 주의하세요. 인터페이스 옵션 이름이 바뀌었다고 하위 설정을 즉시 삭제하지 마세요. 먼저 새로 생성된 실행 파일을 내보내고 기존 설정과 최상위 구간별로 비교하세요.
데스크톱에서 다른 데스크톱 플랫폼으로 옮길 때 서버 프로토콜 매개변수는 보통 구독으로 복원할 수 있지만, 로컬 수신 포트, 시스템 프록시, TUN 권한과 로그 경로는 새 플랫폼에 맞게 다시 설정해야 합니다. Android에서 v2rayNG 또는 v2flyNG로 옮길 때도 먼저 구독을 가져온 뒤 대상 커널에 맞춰 라우팅과 DNS를 복원하세요. 플랫폼 차이는 주로 시스템 인계와 파일 위치에 있으므로 서버 아웃바운드에 플랫폼과 무관한 추측성 필드를 추가해서는 안 됩니다.
고정된 문제 해결 체크리스트 만들기
JSON, 필드 계층, 포트 사용 여부, 태그 참조, 실행 권한
애플리케이션 프록시 유형, 로컬 포트, 시스템 프록시, TUN 인계, 접근 로그
도메인 또는 IP, DNS 반환 결과, 규칙 순서, Geo 데이터, 최종 아웃바운드 태그
서버 접근성, 인증 매개변수, 전송 방식, 보안 계층과 핸드셰이크 로그
모든 장애를 이 네 계층 순서대로 기록하면 같은 작업을 반복하지 않을 수 있습니다. 로그에 rejected가 나타나면 거부가 로컬 인바운드, 라우팅 차단 또는 원격에서 발생했는지 확인하세요. timeout이 나타나면 타임아웃 단계를 확인합니다. 인증이나 핸드셰이크 오류가 나타나면 해당 아웃바운드 매개변수로 돌아가세요. 그래도 판단하기 어렵다면 문제 해결에서 기본 이해, 설치 및 설정, 고급 활용과 문제 해결 분류를 따라 계속 찾아보세요.
설정 유지 관리의 목표는 선택 필드를 가득 채우는 것이 아니라 각 구간의 역할을 명확히 하고 로그로 처리 결과를 입증할 수 있게 하는 것입니다. 먼저 단순하고 반복 검증이 가능한 기준선을 유지한 뒤 라우팅, DNS, TUN과 정책을 추가하세요. 문제가 생기면 복잡한 설정에서 무작위로 필드를 삭제하기보다 기준선으로 돌아가 계층별로 복원하는 편이 안정적입니다.