V2Ray JSON 설정 파일 구조 해설: inbounds·outbounds·routing의 역할
실제 구성 예시를 바탕으로 inbounds, outbounds, routing 세 영역의 주요 필드와 연결 관계를 단계별로 살펴보고, 직접 설정을 읽고 조정하는 방법을 안내합니다.
V2Ray JSON 설정은 실행 순서대로 나열된 스크립트가 아니라 연결 흐름을 정의한 표입니다. 애플리케이션 트래픽은 먼저 특정 인바운드로 들어오고, 라우팅 규칙이 도메인, 대상 주소, 포트 또는 인바운드 태그를 확인한 뒤 아웃바운드를 선택해 전송합니다. DNS, 로그, 정책 영역은 이 흐름에 이름 해석, 상태 확인, 동작 제어 기능을 더합니다. 설정을 서버 주소만 보고 읽으면 트래픽 경로를 실제로 결정하는 태그 관계를 놓치기 쉽습니다.
구독은 가져올 수 있지만 생성된 설정을 이해하기 어려운 독자를 위한 글입니다. 최상위 구조부터 인바운드 리스닝, 프록시·직접 연결 아웃바운드, 라우팅 매칭 순서, DNS 연동 방식을 차례로 확인하고 설정 검토에 바로 사용할 수 있는 필드 목록을 제공합니다.
전체 흐름부터 이해하기: JSON 영역은 서로 독립적이지 않다
V2Ray 코어는 설정을 읽은 뒤 리스닝 포트, 아웃바운드 처리기, 라우터를 생성합니다. 로컬 브라우저를 예로 들면 브라우저가 요청을 로컬 SOCKS 또는 HTTP 프록시 포트로 전달하고, 인바운드가 연결을 받은 뒤 대상을 식별합니다. routing은 위에서부터 규칙을 확인하고, 일치한 outboundTag가 프록시·직접 연결·차단 아웃바운드를 가리킵니다. 일치하는 규칙이 없으면 일반적으로 outbounds 배열의 첫 번째 항목으로 처리되므로 배열 순서에도 의미가 있습니다.
아래 구조에서는 서버 인증 필드를 생략했지만 영역 사이의 연결 지점은 남겨 두었습니다. inbound의 tag, routing.rules의 inboundTag와 outboundTag, 그리고 outbounds에 대응하는 tag를 중점적으로 확인하세요. 태그는 설정 내부에서 사용하는 이름이므로 자유롭게 정할 수 있지만, 참조할 때는 철자와 대소문자를 완전히 동일하게 써야 합니다.
{
"log": {
"loglevel": "warning"
},
"inbounds": [
{
"tag": "socks-in",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"udp": true
},
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls", "quic"]
}
}
],
"outbounds": [
{
"tag": "proxy",
"protocol": "vmess",
"settings": {}
},
{
"tag": "direct",
"protocol": "freedom"
},
{
"tag": "block",
"protocol": "blackhole"
}
],
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "direct"
},
{
"type": "field",
"network": "tcp,udp",
"outboundTag": "proxy"
}
]
}
}
결론: 태그를 먼저 따라가고 프로토콜 세부 사항은 그다음에 확인
“규칙을 작성했는데 분기가 되지 않을 때”는 먼저 inboundTag, outboundTag와 실제 tag가 서로 정확히 연결되는지 확인한 다음 도메인 및 IP 조건을 점검하세요. 전송 매개변수부터 바꾸는 것보다 문제를 훨씬 쉽게 좁힐 수 있습니다.
inbounds: 무엇이 리스닝하고 어떤 트래픽이 코어로 들어오는가
inbounds는 인바운드 배열이며, 각 객체는 하나의 수신 진입점을 나타냅니다. 데스크톱 프록시에서는 SOCKS, HTTP 또는 클라이언트가 조합해 관리하는 로컬 진입점이 가장 흔합니다. listen은 리스닝 주소, port는 포트, protocol은 연결 해석 방식, settings는 프로토콜별 옵션을 지정합니다. listen이 127.0.0.1이면 로컬 프로그램만 접근할 수 있습니다. 0.0.0.0으로 변경하면 모든 네트워크 인터페이스에서 리스닝하므로, 설정 전에 LAN 접근 범위와 시스템 방화벽 규칙을 확인해야 합니다.
SOCKS 인바운드
- tag
- socks-in
- listen
- 127.0.0.1
- port
- 10808
- protocol
- socks
- udp
- true
브라우저, 터미널 도구 또는 SOCKS5를 지원하는 애플리케이션이 명시적으로 연결할 때 적합합니다.
HTTP 인바운드
- tag
- http-in
- listen
- 127.0.0.1
- port
- 10809
- protocol
- http
- timeout
- 300
시스템 HTTP 프록시 설정을 읽는 데스크톱 프로그램에 적합합니다.
tag는 프로토콜을 바꾸지 않으며 routing과 로그에서 안정적으로 참조하기 위한 이름일 뿐입니다. 하나의 설정에 여러 인바운드를 정의하고 서로 다른 정책을 적용할 수도 있습니다. 예를 들어 socks-in은 기본적으로 프록시를 사용하고 http-in은 내부 네트워크만 연결하도록 구성할 수 있습니다. 이때 라우팅 규칙은 inboundTag로 두 연결 유형을 구분하므로 출처 프로그램을 추측할 필요가 없습니다.
sniffing은 연결 초기에 전달되는 데이터에서 대상 도메인을 복원하는 기능입니다. 브라우저가 도메인을 IP로 먼저 해석한 뒤 연결하면 라우터에는 IP만 보일 수 있습니다. sniffing을 활성화하고 destOverride를 설정하면 코어가 HTTP 요청이나 TLS 핸드셰이크에서 도메인을 가져와 domain 규칙에 매칭할 수 있습니다. 이는 DNS 리졸버가 아니며 모든 도메인 분기 문제를 자동으로 해결하지도 않습니다. 애플리케이션이 식별할 수 없는 방식으로 데이터를 캡슐화하면 라우팅에는 여전히 주소만 보일 수 있습니다.
{
"tag": "http-in",
"listen": "127.0.0.1",
"port": 10809,
"protocol": "http",
"settings": {
"timeout": 300
},
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls"]
}
}
- 포트 충돌: 시작에 실패하고 리스닝 오류가 발생하면 먼저 10808 또는 10809 포트를 다른 프로세스가 사용 중인지 확인하세요.
- 프록시 유형: 애플리케이션에 입력한 SOCKS5·HTTP 유형은 해당 인바운드 프로토콜과 일치해야 하며, 포트 번호만 보고 판단해서는 안 됩니다.
- UDP 옵션: SOCKS 인바운드에서 UDP를 처리해야 한다면 settings.udp를 true로 설정하고, 아웃바운드와 전송 경로가 대상 트래픽을 지원하는지도 확인하세요.
- LAN 공유: listen만 수정하지 말고 클라이언트의 LAN 연결 옵션, 시스템 방화벽, 접근 제어 범위도 함께 확인하세요.
outbounds: 프록시·직접 연결·차단 정의하기
outbounds는 아웃바운드 배열입니다. 프록시 아웃바운드는 트래픽을 원격 경로로 캡슐화하고, freedom 아웃바운드는 코어가 대상에 직접 연결하도록 하며, blackhole 아웃바운드는 일치한 연결을 종료합니다. routing에는 서버 연결 매개변수가 저장되지 않고 outboundTag만 반환됩니다. 실제 주소, 포트, 사용자 인증 정보, 전송 방식, 보안 계층은 해당 아웃바운드에 들어 있습니다.
VMess 프록시 아웃바운드
- tag
- proxy
- protocol
- vmess
- address
- 노드 도메인
- port
- 443
- network
- ws
- security
- tls
사용자 매개변수는 settings에, 전송 및 보안 계층은 streamSettings에 있습니다.
로컬 제어 아웃바운드
- direct
- freedom
- block
- blackhole
- 참조 방식
- outboundTag
- 기본 아웃바운드
- 배열의 첫 항목
사설 주소는 대개 직접 연결하고, 명시적으로 종료해야 하는 대상은 block으로 보낼 수 있습니다.
VMess를 예로 들면 settings.vnext는 서버 목록이고, users에는 id, alterId, security 같은 사용자 매개변수가 저장됩니다. streamSettings는 TCP, WebSocket 등의 전송 방식과 TLS 설정을 설명합니다. 서버가 요구하는 전송 방식, 경로, 호스트명, 포트는 한 세트로 일치해야 합니다. network만 tcp에서 ws로 바꾸고 path와 서버 측 진입점을 함께 맞추지 않으면 호환되는 연결이 구성되지 않습니다.
{
"tag": "proxy",
"protocol": "vmess",
"settings": {
"vnext": [
{
"address": "server.example",
"port": 443,
"users": [
{
"id": "구독에서 제공한 사용자 식별자",
"alterId": 0,
"security": "auto"
}
]
}
]
},
"streamSettings": {
"network": "ws",
"security": "tls",
"wsSettings": {
"path": "/gateway"
},
"tlsSettings": {
"serverName": "server.example"
}
}
}
VLESS는 Xray 코어 설정에서 흔히 사용됩니다. 바깥 구조에는 여전히 tag, protocol, settings, streamSettings가 보이지만 flow와 Reality 같은 필드는 해당 코어와 연결 방식의 기능이므로 임의의 V2Ray 설정에 기계적으로 복사해서는 안 됩니다. v2rayN에서 사용하는 코어는 클라이언트 설정에 따라 달라질 수 있고, v2rayNG는 Xray 코어, v2flyNG는 v2fly 코어를 사용합니다. 구독 생성 내용을 읽을 때는 먼저 현재 코어를 확인한 뒤 해당 코어가 지원하는 필드 범위를 살펴보세요.
- 먼저 outbounds의 프록시 항목 tag가 routing에서 실제로 참조하는 이름인지 확인하세요.
- 그다음 address, port, 사용자 인증 필드를 대조하여 로컬 리스닝 포트를 원격 포트로 잘못 이해하지 않았는지 확인하세요.
- 이후 network, security, path, serverName 등 전송 계층 필드가 서로 맞는 한 세트인지 점검하세요.
- 마지막으로 로그의 핸드셰이크, 시간 초과, 연결 거부 메시지를 확인하고 매개변수를 무작정 계속 바꾸지는 마세요.
결론: 연결성 오류는 계층별로 처리
로컬 포트에 연결할 수 없으면 inbound부터 확인하고, 원격 시간 초과는 outbound 주소와 네트워크를 점검하세요. 특정 도메인만 잘못된 아웃바운드로 나가면 routing을 확인합니다. 계층별로 접근하면 한 번에 여러 변수를 바꾸는 일을 피할 수 있습니다.
routing: 매칭 조건, 규칙 순서, 기본 아웃바운드
routing.rules는 순서가 있는 규칙 배열입니다. 일반적으로 type은 field를 사용하며, 조건에는 domain, ip, port, network, inboundTag, protocol 등이 포함될 수 있습니다. 하나의 규칙에 서로 다른 유형의 조건을 여러 개 넣으면 대체로 모든 조건을 동시에 만족해야 합니다. 같은 필드의 여러 값은 후보 집합으로 처리됩니다. 규칙은 선언된 순서대로 검사되며, 먼저 일치한 규칙의 outboundTag가 결정되면 뒤의 규칙이 이를 덮어쓰지 않습니다.
{
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "direct"
},
{
"type": "field",
"domain": ["domain:intranet.example"],
"outboundTag": "direct"
},
{
"type": "field",
"domain": ["geosite:category-ads-all"],
"outboundTag": "block"
},
{
"type": "field",
"network": "tcp,udp",
"outboundTag": "proxy"
}
]
}
}
이 규칙 묶음은 먼저 사설 주소를 직접 연결하고, 지정한 내부 도메인을 직접 연결한 다음, 일치하는 대상을 종료하고, 마지막으로 tcp,udp를 기본 프록시로 사용합니다. 기본 프록시를 첫 번째 규칙에 두면 대부분의 연결을 먼저 가로채 뒤의 직접 연결 및 차단 규칙이 실행될 기회를 잃습니다. 분기 설정을 검토할 때는 “이 규칙이 있는가?”뿐 아니라 “앞에 더 넓은 조건이 이미 있는가?”도 확인해야 합니다.
| 필드 | 매칭 대상 | 일반적인 작성 예 | 확인할 점 |
|---|---|---|---|
| domain | 대상 도메인 | domain:example.com | 도메인을 가져올 수 있는지, 앞선 규칙에 가로채이지 않는지 |
| ip | 대상 주소 | geoip:private | domainStrategy가 해석을 실행하는지 |
| port | 대상 포트 | 53 또는 80-443 | 여기서는 로컬 인바운드 포트가 아닙니다 |
| network | 전송 유형 | tcp,udp | 넓은 조건은 기본 규칙으로 뒤에 배치 |
| inboundTag | 트래픽 진입점 | socks-in | inbounds의 tag와 일치해야 합니다 |
domainStrategy는 라우팅 단계에서 도메인 및 IP 조건을 처리하는 방식을 결정합니다. AsIs는 원래 대상 기준으로 최대한 매칭하며 IP 규칙을 위해 도메인을 적극적으로 해석하지 않습니다. IPIfNonMatch는 먼저 도메인 규칙을 시도하고 일치하지 않으면 주소를 해석해 IP 규칙을 계속 확인합니다. IPOnDemand는 IP 매칭이 필요할 가능성이 있을 때 더 적극적으로 해석합니다. 이는 속도 옵션이 아니라 규칙 의미를 선택하는 설정입니다. geoip:private 같은 IP 규칙이 있고 인바운드로 도메인이 자주 들어온다면 IPIfNonMatch가 이해하기 쉬운 출발점입니다.
DNS와 라우팅의 연동 방식: 해석 결과가 최종 아웃바운드를 뜻하지는 않는다
dns 영역은 코어가 사용할 DNS 서버, 정적 hosts, 조회 정책을 정의하고 routing은 연결을 어떤 아웃바운드로 보낼지 결정합니다. 둘은 관련되어 있지만 동일한 단계는 아닙니다. 애플리케이션이 직접 DNS 조회를 보내면 해당 조회는 일반 네트워크 트래픽으로 코어에 들어옵니다. 애플리케이션이 이미 대상 IP를 제시한 경우 도메인 규칙이 매칭에 참여할 수 있는지는 sniffing과 연결 안에 식별 가능한 도메인 정보가 있는지에 달려 있습니다.
{
"dns": {
"hosts": {
"domain:internal.example": "192.168.10.20"
},
"servers": [
{
"address": "223.5.5.5",
"port": 53,
"domains": ["geosite:cn"]
},
"1.1.1.1"
],
"queryStrategy": "UseIP"
},
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"port": 53,
"network": "udp",
"outboundTag": "proxy"
}
]
}
}
예시에서는 UDP 53 포트 트래픽을 proxy로 보내지만 모든 DNS 조회 방식을 포괄하지는 않습니다. 애플리케이션이 TCP 53을 사용할 수도 있고, 암호화된 HTTPS 연결로 조회할 수도 있습니다. 더 확실한 점검 방법은 요청이 시스템 리졸버, 애플리케이션 자체, 코어 DNS 중 어디에서 발생하는지 먼저 확인한 뒤, 라우팅 규칙이 포트·도메인·특정 인바운드 중 무엇을 매칭해야 하는지 결정하는 것입니다. DNS 주소 하나를 추가한다고 모든 요청이 자동으로 같은 아웃바운드를 사용하는 것은 아닙니다.
- 도메인 규칙이 전혀 매칭되지 않으면 인바운드에서 sniffing을 활성화했는지, 로그의 대상이 도메인으로 표시되는지 IP로 표시되는지 확인하세요.
- 사설 도메인은 직접 연결해야 하는데 프록시로 나간다면, 명시적인 내부 도메인과 geoip:private 규칙을 넓은 프록시 규칙보다 앞에 배치하세요.
- DNS를 변경했는데 결과가 그대로라면 관련 애플리케이션을 다시 시작하고 시스템 DNS 캐시를 비운 뒤 단일 대상만 테스트하세요.
- 해석은 성공했지만 연결 시간이 초과된다면 DNS 처리는 끝난 것입니다. 다음으로 routing의 선택 결과와 outbound 경로를 확인하세요.
직접 조정하기 전 확인 순서: 변경 사항이 덮어써지지 않게 하기
클라이언트는 일반적으로 구독 노드, 전역 매개변수, 라우팅 설정을 실행 구성으로 합칩니다. v2rayN 7.13.x의 일반 매개변수는 「설정」→「매개변수 설정」에서 확인할 수 있습니다. 먼저 로컬 포트, 코어 유형, 시스템 프록시 모드, DNS 관련 옵션을 기록하고 라우팅은 클라이언트의 라우팅 설정에서 조정하세요. v2rayNG 1.10.x에서는 「설정」에서 로컬 프록시, DNS, 분기 관련 항목을 확인할 수 있습니다. 버전에 따라 메뉴 문구는 달라질 수 있지만 순서는 같습니다. 먼저 설정 출처를 확인하고, 그다음 생성 결과를 확인하세요.
- 복구 가능한 사본 보관: 현재 클라이언트 설정을 내보내거나 수정할 독립 JSON을 복사하고, 기존 모드와 적용 범위를 기록하세요.
- JSON 문법 확인: 쉼표, 따옴표, 대괄호, 중괄호를 점검하세요. JSON에서는 줄 끝 주석을 사용할 수 없고 배열의 마지막 항목 뒤에 불필요한 쉼표를 둘 수도 없습니다.
- 태그 연결 확인: 모든 inbound tag, outbound tag와 규칙 참조를 목록으로 만들고 철자 차이가 없는지 확인하세요.
- 한 번에 한 계층만 수정: 포트 문제는 인바운드만, 노드 문제는 아웃바운드만, 분기 문제는 규칙 순서와 조건만 수정하세요.
- 최소 테스트 세트 구성: 직접 연결되어야 하는 대상, 프록시를 사용해야 하는 대상, 명확히 차단되어야 하는 대상을 각각 하나씩 테스트하고 세 결과를 기록하세요.
- 클라이언트의 영구 설정으로 반영: 임시 수정이 유효한지 확인한 뒤 동일한 설정을 v2rayN, v2rayNG 또는 v2flyNG의 공식 설정 메뉴에 저장하세요.
JSON은 시작되지만 모든 웹사이트가 프록시로 연결됩니다. 어디를 가장 먼저 확인해야 하나요?
먼저 routing.rules의 첫 번째 규칙이 network: tcp,udp 또는 지나치게 넓은 domain 조건으로 모든 연결을 잡고 있지 않은지 확인하세요. 사설 주소, 내부 도메인, 명시적인 직접 연결 규칙을 기본 프록시 규칙보다 앞에 옮긴 뒤 코어를 다시 시작해 테스트하세요.
outbounds의 서버 주소를 바꿨는데 노드를 전환하면 왜 원래대로 돌아오나요?
현재 파일이 구독 노드에서 동적으로 생성되었을 가능성이 높습니다. 클라이언트에 저장된 노드 정보를 수정하거나 노드 목록에서 해당 서버 필드를 업데이트하세요. 실행 폴더의 임시 JSON을 장기 설정의 원본으로 사용하지 마세요.
domain 규칙을 작성했는데 로그에는 IP만 표시됩니다. 어떻게 처리해야 하나요?
해당 inbound의 sniffing.enabled가 true인지 확인하고 destOverride에 실제 트래픽 유형이 포함되어 있는지 점검하세요. 여전히 IP만 표시된다면 애플리케이션이 미리 해석했는지, 연결 안에 복원 가능한 도메인 정보가 없는지도 확인하세요.
10808에는 연결되는데 10809에는 연결되지 않습니다. 노드 문제인가요?
먼저 inbounds에 두 포트가 모두 선언되어 있는지, 10809에 해당하는 protocol이 http인지 확인하세요. 로컬 포트 하나만 실패하는 경우는 대개 리스닝 또는 포트 충돌 문제이며 원격 노드 탓으로 바로 단정할 수 없습니다.
설정에 proxy, direct, block이 모두 있습니다. 실제로 어느 항목이 사용되는지 어떻게 확인하나요?
로그 수준을 일시적으로 info로 설정한 뒤 미리 정한 프록시·직접 연결·차단 대상을 각각 방문하고, 대상 주소와 오류 결과를 함께 보며 규칙을 확인하세요. 완료 후 warning으로 되돌리고 이후 회귀 테스트를 위해 세 종류의 테스트 대상을 보관하세요.
V2Ray 설정을 이해하는 핵심은 모든 필드를 외우는 것이 아니라 각 연결을 “어떤 인바운드로 들어왔고, 어떤 대상을 지녔으며, 어떤 규칙과 일치했고, 어떤 아웃바운드로 나갔는가”로 되짚는 데 있습니다. inbounds는 수신, outbounds는 전송, routing은 선택을 담당하며 DNS와 로그가 해석 및 관찰 기능을 보완합니다. 이 흐름을 따라 확인하면 설정이 구독과 클라이언트에서 자동 생성되더라도 포트 충돌, 태그 연결 끊김, 규칙 가림, 전송 매개변수 불일치 지점을 빠르게 찾을 수 있습니다.