config.json · Xray / V2Fly 코어
V2Ray 설정 파일 레퍼런스
config.json 한 부를 단락별로 설명합니다: 최상위 구조, 인바운드와 아웃바운드, 라우팅 규칙, DNS 해석, 정책 튜닝, 그리고 이 필드들이 v2rayN과 v2rayNG에서 어떻게 생성되는지까지. 조각은 그대로 대조용으로 쓸 수 있습니다.
사용 안내와 읽는 순서
이 페이지와 튜토리얼 페이지의 역할 분담
이 페이지는 V2Ray 설정 파일을 체계적으로 찾아보는 매뉴얼로, config.json 한 부를 최상위 구조부터 각 기능 섹션까지 필드 단위로 설명합니다. 사이트에는 빠른 시작 튜토리얼도 있는데, 그쪽은 구독을 클라이언트에 가져와 모드를 고르고 연결한 뒤 정상 동작을 확인하는 한 가지 일만 다룹니다. 둘의 역할은 분명합니다. 튜토리얼 페이지는 '다음에 어디를 누르는가'를, 이 페이지는 '이 필드는 무슨 뜻이고 어떤 값을 써야 유효하며 잘못 쓰면 어떻게 되는가'를 답합니다. 이미 정상적으로 연결되어 일상적으로 쓰기만 하는 사용자라면 이 페이지를 끝까지 읽을 필요는 없습니다.
연결 경로에서 설정 파일의 위치
세 클라이언트 모두 그래픽 껍데기이고, 실제로 연결을 맺는 것은 각자 내장한 코어입니다. v2rayN 데스크톱 버전은 Xray 코어를 내장하고, v2rayNG는 Xray를, v2flyNG는 V2Fly 코어를 사용합니다. GUI에서 체크하는 모든 항목, 즉 전송 방식, TLS, 멀티플렉싱, 분할 라우팅 스위치는 결국 하나의 JSON 설정으로 변환되고, 코어는 시작할 때 이 JSON을 읽어 inbounds에 따라 로컬 포트를 열고 outbounds에 따라 트래픽을 내보냅니다. 이 변환 관계를 이해하면 문제를 볼 때 먼저 판단할 수 있습니다. GUI 옵션을 잘못 고른 것인지, 생성된 설정 자체에 문제가 있는지.
세 클라이언트의 수동 설정 지원
v2rayN은 노드 편집 창에서 전체 JSON 편집 경로를 제공합니다. 구독으로 가져온 노드는 먼저 내부 구조로 파싱되고, 클라이언트가 현재 설정에 맞춰 다시 생성합니다. v2rayNG는 사용자 지정 설정과 구독 가져오기를 지원하지만 모바일에서 긴 JSON을 편집하기는 불편하므로 데스크톱에서 정리한 뒤 가져오는 방식이 더 흔합니다. v2flyNG는 v2rayNG와 설정 형식이 같은 뿌리이고 차이는 코어 계열입니다. 세 클라이언트의 다운로드 경로는 클라이언트 받기 페이지에 있고, 항목별 차이는 비교 리뷰에서 볼 수 있습니다.
권장 읽기 순서
설정 파일을 처음 접하는 사용자는 2장 구조 개요부터 읽고 '최상위에 어떤 섹션이 있고 그중 어느 두 개가 필수인지'를 먼저 잡으세요. 그다음 필요에 따라 원하는 장으로 건너뛰면 됩니다. 일상 사용에서 가장 자주 등장하는 세 섹션은 outbounds, routing, dns입니다. 아웃바운드는 트래픽이 어떻게 나가는지, 라우팅은 어떤 트래픽이 어느 아웃바운드로 가는지, DNS는 도메인을 어디서 해석하는지를 정합니다. policy는 튜닝 항목이라 대부분의 상황에서 기본값으로 충분하고, 메모리 사용량이나 유휴 연결 회수 시간을 조절할 때만 자세히 보면 됩니다.
필드와 코어 버전의 관계
설정 형식은 코어 버전 사이에서 조금씩 바뀝니다. 새 전송 방식과 새 보안 유형은 보통 Xray 쪽에 먼저 들어오고 V2Fly 쪽이 뒤따릅니다. 이 페이지의 예시는 특정 버전에 묶이지 않은 범용 필드를 중심으로 합니다. 어떤 필드가 현재 클라이언트에서 쓸 수 있는지 판단하는 확실한 방법은 실행 로그를 보는 것입니다. 코어가 모르는 필드는 조용히 적용되는 대신 시작 로그에 오류나 무시 안내를 남깁니다.
예시 값에 대하여
이 페이지의 모든 설정 조각에서 도메인, UUID, 공개키는 교체가 명확한 예시 표기입니다. 실제 설정에 그대로 복사하지 마세요. 노드 파라미터는 서비스 제공자가 알려준 정보를 기준으로 합니다. 페이지 상단의 섹션 바에서 원하는 장으로 바로 갈 수 있고, 각 장은 소절로 나뉩니다. 표는 필드 빠른 조회용, 코드 블록은 전체 조각용, 배경색이 있는 안내 블록은 자주 걸리는 함정용입니다.
JSON 구조 개요
최상위에 어떤 섹션이 있는가
완전한 설정 파일은 하나의 JSON 객체입니다. 최상위에 흔히 오는 섹션은 아홉 개입니다. log는 로그, inbounds는 로컬 수신 지점, outbounds는 트래픽 출구, routing은 분할 규칙, dns는 해석 정책, policy는 연결과 버퍼 정책, stats와 api는 GUI 클라이언트가 상태를 읽기 위한 것, reverse는 리버스 프록시 시나리오용입니다. 이 중 inbounds와 outbounds는 필수입니다. 인바운드가 없으면 앱 트래픽이 들어오지 못하고, 아웃바운드가 없으면 트래픽이 나가지 못합니다.
아래 최소 설정은 필수 항목과 로그 섹션 하나만 남긴 것으로, 구조를 이해하는 데는 쓸 수 있지만 직접 연결만 하고 프록시 기능은 없습니다:
{
"log": { "loglevel": "warning" },
"inbounds": [
{
"tag": "socks-in",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": { "udp": true }
}
],
"outbounds": [
{ "tag": "direct", "protocol": "freedom" }
]
}
필드 이름과 문법 규칙
JSON은 문법에 엄격합니다. 설정 파일에서 가장 흔한 시작 실패는 다음 몇 가지에서 나옵니다. 필드 이름은 대소문자를 구분하므로 outbounds를 outBounds로, streamSettings를 streamsettings로 쓰면 파싱에 실패합니다. 표준 JSON은 주석을 허용하지 않으므로 웹 페이지나 메모에서 설정을 복사할 때 줄 안의 // 주석과 블록 주석을 지워야 합니다. 마지막 항목 뒤의 쉼표도 허용되지 않습니다. 문자열은 반드시 큰따옴표를 쓰고 작은따옴표는 안 됩니다. 포트, 타임아웃 같은 숫자는 따옴표 없이 그대로 쓰고, 불리언은 소문자 true와 false만 됩니다.
tag의 역할
각 인바운드와 아웃바운드는 tag 필드를 가질 수 있습니다. tag는 사용자 지정 문자열이고 routing 규칙이 tag로 특정 인바운드나 아웃바운드를 가리킵니다. 예를 들어 'socks-in에서 온 트래픽은 proxy 아웃바운드로' 같은 식입니다. tag는 socks-in, http-in, proxy, direct, block처럼 읽히는 이름을 쓰는 게 좋습니다. 몇 달 뒤에 설정을 다시 봐도 대응이 됩니다. 같은 설정 안에서 tag가 중복되면 규칙이 어디를 가리키는지 불분명해지므로 피하세요.
| 최상위 필드 | 역할 | 필수 여부 | 흔한 표기 |
|---|---|---|---|
| log | 로그 레벨과 출력 위치 | 선택 | {"loglevel":"warning"} |
| inbounds | 로컬 수신 지점 | 필수 | 배열, 최소 한 항목 |
| outbounds | 트래픽 출구 | 필수 | 배열, 첫 항목이 기본 아웃바운드 |
| routing | 분할 규칙 | 선택 | {"domainStrategy":"IPIfNonMatch","rules":[]} |
| dns | 도메인 해석 정책 | 선택 | {"servers":[]} |
| policy | 연결과 버퍼 정책 | 선택 | {"levels":{"0":{}}} |
| stats / api | 통계와 로컬 인터페이스 | 선택 | GUI 클라이언트가 필요할 때 생성 |
로드와 적용 방식
코어는 시작할 때 설정을 한 번 읽습니다. 설정을 바꾸면 코어를 재시작하거나 리로드를 트리거해야 하는데, GUI 클라이언트는 보통 노드를 저장할 때 이 단계를 자동으로 처리합니다. 설정 오류는 두 가지로 나타납니다. 하나는 파싱 실패로 코어가 바로 종료되고 로그에 오류 위치가 찍히는 경우, 다른 하나는 필드는 유효하지만 의미가 충돌해 코어는 뜨는데 연결 동작이 예상과 다른 경우입니다. 예를 들어 라우팅 규칙 순서가 뒤바뀌어 분할이 적용되지 않는 식입니다. 전자는 찾기 쉽고, 후자는 로그를 대조하며 한 단락씩 걸러내야 합니다.
구독과의 관계
구독에 있는 각 노드는 결국 이런 설정 한 부로 펼쳐집니다. 클라이언트는 노드마다 별도의 outbounds 항목을 만들고 인바운드, 라우팅, DNS 섹션을 덧붙여 코어가 읽을 수 있는 완전한 파일을 조립합니다. 그래서 구독을 갱신하면 클라이언트가 설정 전체를 다시 생성하고, 손으로 고친 필드는 덮어써집니다. 이 부분은 뒤의 클라이언트 장에서 더 자세히 다룹니다.
클라이언트마다 설정 파일을 두는 위치가 다릅니다. 데스크톱은 보통 프로그램 디렉터리나 사용자 설정 디렉터리 아래이고, Android는 앱 내부에서 관리합니다. 일상 사용에서는 경로를 몰라도 되고, 설정을 내보내 백업하거나 옮길 때만 찾으면 됩니다.
inbounds 인바운드
인바운드가 하는 일
인바운드는 코어가 로컬에서 무엇을 수신하는지, 즉 앱 트래픽이 코어로 들어오는 입구를 정의합니다. GUI 클라이언트는 보통 두 항목을 자동 생성합니다. socks 인바운드는 브라우저와 시스템 프록시용, http 인바운드는 HTTP 프록시만 지원하는 프로그램용입니다. 둘은 서로 다른 포트를 수신하며 동시에 존재할 수 있습니다. 아래는 스니핑 설정을 포함한 이중 인바운드 구성입니다:
"inbounds": [
{
"tag": "socks-in",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": { "auth": "noauth", "udp": true },
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls"],
"routeOnly": false
}
},
{
"tag": "http-in",
"listen": "127.0.0.1",
"port": 10809,
"protocol": "http"
}
]
필드 하나씩 설명
- tag
- 인바운드의 식별 이름으로 라우팅 규칙에서 참조합니다. 같은 설정 안에서 중복되면 안 됩니다.
- listen
- 수신 주소. 127.0.0.1은 로컬 접속만 허용하고, 0.0.0.0은 같은 네트워크의 다른 기기 접속을 허용합니다.
- port
- 수신 포트. 시스템의 다른 프로그램과 충돌하면 코어 시작이 실패하고 로그에 수신 실패가 표시됩니다.
- protocol
- 인바운드 프로토콜. 흔한 값은 socks, http, dokodemo-door입니다.
- settings
- 프로토콜 관련 파라미터. socks의 udp는 UDP 트래픽 전달 여부를 정하고, http 인바운드는 보통 추가 설정이 필요 없습니다.
- sniffing
- 트래픽에서 실제 목적지 도메인을 식별합니다. destOverride는 덮어쓸 수 있는 프로토콜 유형을 지정하고, routeOnly가 true면 라우팅 판단에만 쓰고 목적지 주소는 바꾸지 않습니다.
sniffing이 필요한 이유
앱이 socks로 목적지 주소를 넘길 때 도메인이 아니라 IP가 넘어올 수 있습니다. 목적지가 IP 형태로 오면 routing의 도메인 기반 규칙이 모두 무효가 되어 분할이 부정확해집니다. sniffing을 켜면 코어가 TLS 핸드셰이크의 SNI, HTTP 요청 헤더의 Host에서 실제 도메인을 추출해 그 도메인으로 라우팅 규칙을 매칭합니다. destOverride에는 덮어쓸 수 있는 프로토콜 유형을 나열하며 흔한 표기는 http와 tls입니다. routeOnly를 true로 두면 추출한 도메인을 라우팅 판단에만 쓰고 실제 요청의 목적지 주소는 바꾸지 않으므로 원래 요청 형태를 유지해야 하는 상황에 맞습니다. 데스크톱과 모바일 클라이언트 모두 기본적으로 스니핑을 켭니다.
dokodemo-door 설명
dokodemo-door는 또 다른 종류의 인바운드로, 로컬 포트로 들어온 트래픽을 지정한 목적지로 그대로 전달합니다. LAN 기기나 특정 포트의 요청을 코어로 끌어들일 때 자주 씁니다. GUI 클라이언트에는 대응하는 스위치가 없어 직접 설정을 쓰는 영역이고, address와 port를 스스로 지정해야 합니다.
| 인바운드 프로토콜 | 용도 | 주로 나타나는 위치 |
|---|---|---|
| socks | 브라우저와 시스템 프록시의 표준 입구 | v2rayN, v2rayNG가 기본 생성 |
| http | HTTP 프록시만 지원하는 프로그램 | v2rayN이 기본 생성 |
| dokodemo-door | 포트 포워딩과 LAN 접속 | 수동 설정 |
수신 주소의 보안 경계
listen을 127.0.0.1로 쓰면 로컬에서만 이 포트에 접속할 수 있고 이것이 기본값입니다. 0.0.0.0으로 바꾸면 같은 네트워크의 다른 기기가 이 컴퓨터를 프록시로 지정할 수 있습니다. 프록시 공유가 분명히 필요한 상황에 맞지만 네트워크 환경이 신뢰할 수 있어야 합니다. GUI 클라이언트의 해당 옵션은 보통 'LAN에서의 연결 허용'으로 표시되니 켜기 전에 이 점을 확인하세요. 프록시 포트 자체에 인증이 없으면 그 포트에 접근할 수 있는 기기는 누구나 그대로 사용할 수 있습니다.
포트가 이미 사용 중
인바운드 포트가 점유되어 있으면 코어 시작이 실패하고 로그에 수신 실패가 표시됩니다. 처리 방법은 세 가지입니다. 인바운드 포트를 바꾸거나, 포트를 점유한 프로세스를 찾아 종료하거나, 점유한 쪽이 제대로 종료되지 않은 이전 코어 프로세스라면 클라이언트를 재시작하면 됩니다. 포트를 바꾼 뒤에는 시스템 프록시나 브라우저 프록시 설정도 함께 갱신해야 합니다. 그렇지 않으면 앱이 계속 예전 포트로 요청을 보냅니다.
outbounds 아웃바운드
아웃바운드가 하는 일
아웃바운드는 트래픽이 코어를 떠난 뒤 어떻게 가는지를 정합니다. outbounds는 배열이고 여러 항목을 담을 수 있으며 각각 자기 tag를 가집니다. 배열의 첫 항목이 기본 아웃바운드이고, 어떤 라우팅 규칙에도 걸리지 않은 트래픽이 이쪽으로 갑니다. 아래는 TCP에 REALITY를 조합한 VLESS 아웃바운드 예시입니다:
"outbounds": [
{
"tag": "proxy",
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "node.example.com",
"port": 443,
"users": [
{
"id": "00000000-0000-0000-0000-000000000000",
"encryption": "none",
"flow": "xtls-rprx-vision"
}
]
}
]
},
"streamSettings": {
"network": "tcp",
"security": "reality",
"realitySettings": {
"serverName": "node.example.com",
"fingerprint": "chrome",
"publicKey": "your-public-key",
"shortId": "your-short-id"
}
},
"mux": { "enabled": false }
},
{ "tag": "direct", "protocol": "freedom" },
{ "tag": "block", "protocol": "blackhole" }
]
서버 파라미터
vnext 배열은 원격 서버를 기술하며 각 항목은 address, port, users를 가집니다. address는 도메인도 IP도 가능하고, port는 서버가 수신하는 포트입니다. users의 id는 신원 식별자로 VMess와 VLESS 모두 UUID 형식을 씁니다. VLESS의 encryption 필드는 none으로 고정하며 암호화는 전송 계층이 담당합니다. VMess의 alterId는 최신 버전에서 기본 0이고, 예전 노드가 여전히 0이 아닌 값을 요구한다면 잘못 채웠을 때 바로 연결 실패로 이어집니다. flow는 VLESS의 흐름 제어 옵션으로 전송 계층 보안 유형과 함께 쓰이며, tcp 전송에 TLS나 REALITY를 조합할 때 흔한 표기는 xtls-rprx-vision입니다.
전송 계층 파라미터
- network
- 전송 방식. 흔한 값은 tcp, ws, grpc, httpupgrade이며 서버와 반드시 같아야 합니다.
- security
- 전송 계층 보안 유형. none은 암호화 없음, tls는 표준 TLS, reality는 REALITY입니다.
- wsSettings
- WebSocket 전용 파라미터. path는 요청 경로, headers.Host는 요청 헤더의 호스트 이름입니다.
- tlsSettings
- TLS 전용 파라미터. serverName은 인증서에 대응하는 도메인, allowInsecure는 인증서 검증을 건너뛰며 장기간 켜 두는 것은 권장하지 않습니다.
- realitySettings
- REALITY 전용 파라미터. serverName, publicKey, shortId 세 가지는 서버와 완전히 일치해야 하고, fingerprint는 클라이언트 지문이 드러나는 방식을 정합니다.
아래는 WebSocket에 TLS를 조합한 전송 계층 표기로, 위의 REALITY 예시와 같은 층위입니다. streamSettings 블록 전체를 바꾸면 됩니다:
"streamSettings": {
"network": "ws",
"security": "tls",
"wsSettings": {
"path": "/your-path",
"headers": { "Host": "node.example.com" }
},
"tlsSettings": {
"serverName": "node.example.com",
"allowInsecure": false
}
}
VMess 아웃바운드의 차이
VMess 아웃바운드는 구조가 VLESS와 같고 users 안의 필드가 다릅니다. VMess는 alterId와 security 두 필드로 암호화 방식을 기술하는데, 전자는 최신 버전에서 기본 0이고 후자의 흔한 표기는 auto입니다. 전송 계층 파라미터는 VLESS와 완전히 호환되므로, 같은 서버가 두 프로토콜을 함께 제공한다면 전환할 때 protocol과 users 두 곳만 바꾸면 됩니다. VMess와 VLESS가 신원 인증과 전송 의존성에서 어떻게 다른지는 프로토콜 기초에 네 가지 대조로 정리되어 있습니다.
mux 멀티플렉싱
mux는 멀티플렉싱 스위치입니다. 켜면 코어가 여러 연결을 하나의 하위 연결로 합쳐 반복 핸드셰이크를 줄이고, 네트워크 상태가 안정적인 링크에서는 지연을 낮출 수 있습니다. 대신 연결 하나의 품질이 서로 영향을 주므로 대용량 파일 다운로드처럼 지속적으로 높은 처리량이 필요한 상황에서는 오히려 느려질 수 있습니다. 클라이언트 기본값은 꺼짐이므로 필요할 때 켜면 됩니다.
freedom과 blackhole
freedom은 직접 연결 아웃바운드로, 받은 것을 그대로 내보냅니다. 국내 도메인과 사설 주소를 처리할 때 라우팅 규칙과 함께 자주 씁니다. sendThrough 필드로 로컬의 어느 주소에서 내보낼지 지정할 수 있습니다. blackhole은 버리는 아웃바운드로 특정 트래픽을 차단할 때 쓰며 반환 내용을 설정할 수 있습니다. 둘 다 서버 파라미터가 필요 없고 tag 하나만 쓰면 됩니다.
| 아웃바운드 프로토콜 | 용도 | 비고 |
|---|---|---|
| vless | 주력 프록시 아웃바운드 | 내장 암호화 없음, 전송 계층 보안 유형에 의존 |
| vmess | 초기 노드 호환 | alterId는 최신 버전에서 기본 0 |
| freedom | 직접 연결 | 로컬 출구 주소 지정 가능 |
| blackhole | 차단과 폐기 | 반환 내용 설정 가능 |
전송 파라미터는 항목별로 맞춰야 함
경로, Host, SNI, 공개키, shortId 같은 파라미터는 서버와 완전히 같아야 합니다. 하나라도 어긋나면 명확한 오류 대신 연결이 맺어진 직후 바로 끊기는 형태로 나타납니다. 문제를 볼 때는 복사 과정에서 생긴 여분의 공백이나 빠진 슬래시를 먼저 의심하세요.
routing 라우팅 규칙
규칙이 적용되는 방식
routing은 어떤 트래픽이 어느 아웃바운드로 가는지 정합니다. 구조는 domainStrategy와 rules 두 부분입니다. 규칙은 위에서 아래로 차례로 매칭되고 처음 걸린 규칙이 적용되며 뒤의 규칙은 더 이상 판단하지 않습니다. 그래서 규칙의 개수보다 순서가 중요합니다. rules 배열의 각 항목은 객체이고 type은 field로 고정, 나머지 필드가 매칭 조건과 매칭 후 동작을 기술합니다.
domainStrategy의 세 가지 값
- AsIs
- 들어온 도메인이나 IP를 그대로 매칭하고 해석하지 않습니다. 가장 빠르지만 순수 IP 형태의 요청은 도메인 규칙에 걸리지 않습니다.
- IPIfNonMatch
- 도메인 규칙이 걸리지 않으면 도메인을 IP로 해석해 IP 규칙으로 한 번 더 매칭합니다. 일상 사용에서 가장 흔한 값입니다.
- IPOnDemand
- 규칙에 IP 조건이 하나라도 있으면 즉시 해석합니다. 판단이 가장 완전하지만 해석 비용도 가장 큽니다.
규칙 조건 필드
| 조건 필드 | 표기 예시 | 설명 |
|---|---|---|
| domain | ["domain:example.com"] | 도메인으로 매칭, 여러 접두사 표기를 지원 |
| ip | ["geoip:private"] | 목적지 IP 또는 IP 대역으로 매칭 |
| port | "443" 또는 "0-65535" | 목적지 포트로 매칭, 범위 지원 |
| sourcePort | "1-65535" | 출발지 포트로 매칭 |
| inboundTag | ["socks-in"] | 트래픽이 어느 인바운드에서 왔는지로 구분 |
| network | "tcp" 또는 "udp" | 전송 계층 프로토콜로 구분 |
| protocol | ["http","tls"] | 스니핑 결과에 의존하므로 sniffing을 먼저 켜야 함 |
| outboundTag | "direct" | 매칭 후 동작: 어느 아웃바운드로 갈지 |
| balancerTag | "auto" | 매칭 후 동작: 로드 밸런싱 사용 |
도메인 매칭의 네 가지 접두사
- domain:
- 해당 도메인과 모든 하위 도메인을 매칭합니다. domain:example.com은 example.com과 a.example.com을 함께 매칭합니다.
- full:
- 정확히 일치. full:example.com은 example.com만 매칭하고 하위 도메인은 포함하지 않습니다.
- keyword:
- 키워드가 포함되면 매칭. 범위가 가장 넓어 오탐이 나기 쉬우므로 앞의 두 가지로 표현할 수 없을 때만 씁니다.
- regexp:
- 정규식 매칭. 표기가 유연하지만 요청마다 정규식을 한 번 돌아야 해서 규칙이 많아지면 판단이 느려집니다.
아래는 바로 쓸 수 있는 분할 조각입니다. 사설 주소와 국내 도메인은 직접 연결, UDP 443 포트는 차단, 나머지 트래픽은 기본 아웃바운드로 보냅니다.
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"domain": ["geosite:private"],
"outboundTag": "direct"
},
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "direct"
},
{
"type": "field",
"domain": ["geosite:cn"],
"outboundTag": "direct"
},
{
"type": "field",
"network": "udp",
"port": "443",
"outboundTag": "block"
}
]
}
순서를 뒤집었을 때의 전형적인 결과
순서를 뒤집은 예를 하나 들면, 첫 규칙에 '모든 트래픽은 proxy로', 두 번째 규칙에 '국내 도메인은 직접 연결'을 쓴 경우입니다. 첫 규칙이 이미 모든 트래픽을 잡았으므로 두 번째는 영원히 판단되지 않고 분할 효과가 없는 것과 같습니다. 올바른 표기는 구체적인 조건을 앞에, 넓은 조건을 뒤에 두고 마지막에 기본 규칙을 추가하는 것입니다. 순서 문제를 판단할 때는 로그 레벨을 잠시 debug로 올리면 코어가 연결마다 매칭 결과를 찍어 주므로 어느 규칙에 걸렸는지 바로 볼 수 있습니다.
geosite와 geoip 데이터
geosite:cn, geoip:private 같은 표기는 클라이언트에 내장되거나 내려받은 규칙 데이터 파일에 의존합니다. 데이터에는 갱신 주기가 있어 새로 생긴 도메인이 아직 포함되지 않았을 수 있고, 그럴 때는 일부 사이트의 분할 결과가 예상과 다르게 나옵니다. GUI 클라이언트는 보통 라우팅 설정에 데이터 갱신 경로를 제공하니 주기적으로 한 번씩 갱신하면 됩니다. 사설 주소 대역 데이터는 거의 바뀌지 않아 자주 갱신할 필요가 없습니다.
balancer 개요
balancer는 여러 아웃바운드 사이에 트래픽을 분배할 때 쓰고 규칙에서 balancerTag로 참조합니다. 먼저 balancer 섹션을 정의하고 selector로 tag 접두사에 맞는 아웃바운드를 골라낸 뒤 분배 전략을 지정해야 합니다. 일반 사용자에게는 거의 필요 없고, 동등한 노드를 여러 개 운영할 때만 쓰입니다.
라우팅이 적용되지 않을 때 점검 순서
차례로 확인하세요. sniffing이 켜져 있는지(도메인 규칙이 여기에 의존합니다), 목적지가 IP 형태로 들어오는지(IP 형태는 도메인 규칙에 걸리지 않습니다), 규칙 순서가 더 넓은 조건에 가려졌는지, 규칙 데이터 파일을 갱신해야 하는지. 네 가지가 모두 정상이면 로그의 실제 매칭 기록을 보세요.
dns 설정
dns 섹션을 쓸 때와 쓰지 않을 때의 차이
dns 섹션은 코어가 도메인을 어떻게 해석하는지 정합니다. 이 섹션을 쓰지 않으면 코어는 해석을 시스템에 맡기고, 쓰면 설정의 서버 목록과 정책에 따라 직접 질의를 보냅니다. 이 섹션을 쓰는 가치는 세 가지입니다. 해석 결과가 중간 단계에서 간섭받지 않게 하고, 도메인 기반 분할을 더 정확하게 만들고, 불필요한 해석 왕복 한 번을 줄입니다. 아래는 자주 쓰는 필드의 전체 표기입니다:
"dns": {
"hosts": {
"domain:node.example.com": "203.0.113.10"
},
"queryStrategy": "UseIPv4",
"servers": [
{
"address": "223.5.5.5",
"domains": ["geosite:cn"],
"expectIPs": ["geoip:cn"]
},
{
"address": "1.1.1.1",
"domains": ["geosite:geolocation-!cn"]
}
]
}
- servers
- DNS 서버 목록. 항목은 주소 문자열만 쓸 수도 있고 객체로 쓸 수도 있으며, domains로 그 서버가 담당할 도메인을 지정하고 expectIPs로 반환 결과가 예상 대역 안에 있는지 검증합니다.
- hosts
- 정적 매핑으로 도메인을 고정 IP로 바로 연결하고 해석 단계를 건너뜁니다. domain:, full:, keyword: 접두사를 지원하며 표기는 라우팅 규칙과 같습니다.
- queryStrategy
- 우선 해석할 주소 계열을 제어합니다. UseIP는 제한 없음, UseIPv4와 UseIPv6는 각각 해당 주소 계열만 남깁니다.
- domains
- 서버 객체 안에 쓰며 이 서버가 어떤 도메인만 처리할지 한정합니다. 위 예시에서 첫 번째는 국내 도메인, 두 번째는 나머지 도메인을 담당합니다.
- expectIPs
- 반환 결과를 한 번 검증해 해석된 주소가 지정 대역 밖이면 버립니다. 해석 결과가 바뀌는 것을 막는 데 씁니다.
hosts의 용도
hosts는 정적 매핑으로 도메인을 고정 IP로 바로 연결해 해석 단계를 생략합니다. 두 가지 상황에 맞습니다. 노드 도메인이 알려져 있고 안정적이어서 해석을 건너뛰고 싶을 때, 테스트 환경에서 특정 도메인을 지정 주소로 고정하고 싶을 때입니다. 표기는 domain:, full:, keyword: 접두사를 지원하고 라우팅 규칙의 도메인 표기와 완전히 같아 따로 외울 필요가 없습니다.
queryStrategy 선택 기준
queryStrategy는 우선 해석할 주소 계열을 제어합니다. UseIP는 제한 없이 서버가 돌려주는 순서를 따르고, UseIPv4와 UseIPv6는 각각 해당 주소 계열만 남깁니다. IPv4 출구만 있는 환경에서 UseIPv4를 쓰면 IPv6 주소가 해석된 뒤 연결이 안 되어 재시도하는 일을 막아 실패 왕복 한 번을 줄일 수 있습니다. 반대로 로컬 네트워크가 이미 IPv6 중심이라면 UseIPv6를 써서 불필요한 듀얼 스택 질의를 줄일 수 있습니다.
라우팅과의 협력
DNS 질의는 코어가 직접 보내며 outbounds의 프록시 경로를 거치지 않으므로 해석을 위한 별도 라우팅 규칙은 필요 없습니다. 다만 해석 결과가 라우팅에 영향을 준다는 점은 유의해야 합니다. IPIfNonMatch를 켜면 도메인이 먼저 IP로 해석된 뒤 IP 규칙에 매칭되므로, 해석 결과가 부정확하면 IP 규칙도 함께 부정확해집니다. dns 섹션과 routing 섹션을 같이 봐야 하는 이유입니다.
모드별로 누가 해석하는가
데스크톱에서 시스템 프록시를 쓸 때는 도메인 해석을 운영체제가 계속 담당하고, 코어의 dns 섹션은 코어가 스스로 해석해야 할 때만 작동합니다. TUN 모드를 켜면 코어가 DNS 요청을 포함한 모든 트래픽을 넘겨받습니다. Android의 v2rayNG는 VpnService로 동작하며 마찬가지로 DNS 질의를 넘겨받습니다. 그래서 같은 dns 설정이라도 클라이언트와 모드에 따라 실제 영향 범위가 다르며, 효과를 판단할 때는 지금 어떤 모드인지 먼저 확인해야 합니다.
해석 결과와 분할의 관계
분할이 부정확할 때는 도메인 규칙이 안 먹은 것인지 IP 규칙이 안 먹은 것인지부터 가르세요. 전자는 대개 스니핑과 관련되고 후자는 대개 해석 결과와 관련됩니다. 이 두 가지 원인을 나누면 점검 범위가 훨씬 좁아집니다.
policy 정책
policy를 써야 할 때
policy는 연결의 수명 주기와 버퍼 크기를 제어하는 튜닝 항목입니다. 기본값은 일반적인 사용에 충분하고, 이 섹션을 쓰는 전형적인 상황은 세 가지입니다. 메모리 사용량을 줄이기, 유휴 연결 회수를 빠르게 하기, 인바운드별로 다른 제한을 두기. 아래는 흔한 표기입니다:
"policy": {
"levels": {
"0": {
"handshake": 4,
"connIdle": 300,
"uplinkOnly": 2,
"downlinkOnly": 5,
"bufferSize": 512
}
},
"system": {
"statsInboundUplink": true,
"statsInboundDownlink": true
}
}
| 필드 | 단위 | 역할 |
|---|---|---|
| handshake | 초 | 핸드셰이크 단계의 타임아웃, 넘으면 실패로 판정 |
| connIdle | 초 | 연결이 유휴 상태로 얼마나 있다가 회수되는지 |
| uplinkOnly | 초 | 하향이 닫힌 뒤 상향을 유지하는 시간 |
| downlinkOnly | 초 | 상향이 닫힌 뒤 하향을 유지하는 시간 |
| bufferSize | KB | 연결당 버퍼 크기, 0은 버퍼를 쓰지 않음 |
levels 사용법
levels의 키는 등급 번호이고 0이 기본 등급입니다. 인바운드의 settings나 사용자 설정에서 level 필드를 지정해 특정 인바운드나 사용자를 특정 등급으로 묶어 입구별로 제한을 다르게 둘 수 있습니다. 일상 사용에서는 0 등급만 바꾸면 충분하고, 입구마다 다른 등급을 배정하는 것은 다중 사용자 시나리오의 방식입니다.
system 섹션
system 섹션은 통계 스위치를 제어합니다. statsInboundUplink와 statsInboundDownlink를 켜면 코어가 인바운드 방향의 상하향 트래픽을 집계하고, GUI 클라이언트의 속도 표시가 이 데이터에 의존합니다. statsOutboundUplink와 statsOutboundDownlink는 아웃바운드 방향에 대응합니다. 통계를 끄면 약간의 오버헤드를 아낄 수 있지만 화면의 트래픽 수치가 더 이상 갱신되지 않습니다.
튜닝의 트레이드오프
connIdle을 낮추면 유휴 연결이 더 빨리 회수되어 메모리 사용량이 줄지만, 다시 쓸 때 연결을 새로 맺어야 해서 핸드셰이크 지연이 한 번 더 생깁니다. bufferSize는 키우면 고대역폭 상황에 도움이 되고 줄이면 메모리를 아낍니다. handshake, uplinkOnly, downlinkOnly는 보통 조정할 필요가 없고, 네트워크 품질이 나빠 핸드셰이크가 자주 타임아웃되는 환경에서만 handshake를 늘려 볼 만합니다.
모바일의 특수한 상황
모바일에서 백그라운드 연결이 시스템에 의해 회수되는 일은 대개 policy와 무관하고 절전 정책이 원인입니다. 관련 처리는 v2rayNG 사용 요점에 설명되어 있습니다. 튜닝의 전제는 안정적인 기준선이 있느냐입니다. 기본 설정 한 부를 남겨 두고, 바꾼 뒤에는 항목별로 비교해 이득이 실제로 있는지 확인한 다음 유지하세요. 인터넷에 도는 파라미터 조합은 대부분 특정 하드웨어와 특정 상황을 전제로 하므로 그대로 옮겨 온다고 개선이 보장되지는 않습니다.
클라이언트에서의 설정 생성과 수동 편집
구독이 설정으로 펼쳐지는 방식
구독 주소가 돌려주는 것은 인코딩된 텍스트 한 덩어리이고, 클라이언트는 내려받은 뒤 줄 단위로 파싱합니다. 각 줄이 노드 하나의 전체 파라미터를 기술하고, 클라이언트는 그 파라미터를 outbounds의 항목 하나로 번역합니다. 구독의 노드 수가 outbounds 배열의 길이에 대응하고, 구독을 갱신하면 설정 전체가 다시 생성됩니다. 이 경로를 이해하면 GUI에서 고친 노드가 갱신 후 원래대로 돌아가는 이유를 알 수 있습니다.
공유 링크 파라미터와 설정 필드의 대응
| 링크 파라미터 | 대응 설정 필드 | 설명 |
|---|---|---|
| add / address | vnext[].address | 서버 주소 |
| port | vnext[].port | 서버 포트 |
| id / uuid | users[].id | 신원 식별자 |
| flow | users[].flow | 흐름 제어 옵션, VLESS에서만 사용 |
| net | streamSettings.network | 전송 방식 |
| tls | streamSettings.security | 보안 유형 |
| host | wsSettings.headers.Host | WebSocket 요청 헤더 호스트 이름 |
| path | wsSettings.path | WebSocket 요청 경로 |
| sni | tlsSettings.serverName | TLS 도메인 |
| type | tlsSettings.fingerprint | 클라이언트 지문 표시 방식 |
v2rayN의 편집 경로
v2rayN은 노드 목록 우클릭 메뉴에 편집 경로가 있고, 창은 기본 필드와 전송 필드로 나뉩니다. 클라이언트 GUI가 노출하지 않는 필드를 고쳐야 할 때는 파라미터 설정에서 전체 설정 편집 경로를 찾을 수 있습니다. 구독 갱신은 구독 전체를 기준으로 노드 목록을 교체하므로 손으로 고친 노드는 다음 갱신 때 덮어써진다는 점을 기억하세요. 오래 유지할 조정은 별도 노드로 저장하거나 먼저 내보내 백업하는 편이 좋습니다.
v2rayNG의 설정 출처
v2rayNG의 노드 출처는 세 가지입니다. QR 코드 스캔, 클립보드에서 공유 링크 가져오기, 구독 가져오기. 모바일에서 긴 JSON을 편집하기는 불편하므로 데스크톱에서 정리한 뒤 가져오는 방식이 흔합니다. 앱별 프록시는 설정에서 앱을 체크해 지정하며 어떤 앱의 트래픽이 VpnService로 들어오는지만 정하고 JSON 설정 자체와는 무관합니다. 즉 앱별 프록시 규칙은 config.json에 나타나지 않습니다.
v2flyNG의 위치
v2flyNG는 v2rayNG와 설정 형식이 같은 뿌리이고 차이는 코어 계열입니다. 같은 노드의 파라미터를 양쪽에서 모두 쓸 수 있고, 특정 코어 버전이 특정 전송 방식을 지원하는 데 차이가 있을 때 대안 클라이언트로 대조할 수 있습니다. 세 클라이언트의 플랫폼과 버전 경로는 클라이언트 받기 페이지에 있습니다.
어떤 경우에 설정을 직접 고칠 만한가
세 가지 상황이 직접 고칠 만합니다. 클라이언트 GUI가 특정 필드를 노출하지 않을 때(예: 사용자 지정 라우팅 규칙, bufferSize), 앱별이나 포트별로 분할을 따로 두고 싶을 때, 문제를 최소 설정으로 재현하고 싶을 때입니다. 앞의 두 가지는 데스크톱에서 끝내고 내보낸 뒤 모바일로 동기화하는 편이 좋습니다. 세 번째 상황에서는 손으로 쓰는 설정이 짧을수록 좋고, 문제 재현에 필요한 섹션만 남기세요.
구독 형식의 더 자세한 내용
구독에는 흔한 세 가지 형태가 있습니다. base64로 인코딩된 링크 목록, 원본 JSON 설정, 단일 공유 링크입니다. 세 가지의 차이와 변환할 때 필드가 유실되기 쉬운 지점은 구독 형식 기초에 항목별로 대조해 두었습니다. 가져오기에 실패하면 구독이 어떤 형태를 돌려주는지 먼저 확인하고, 그에 맞는 경로로 가져오세요.
문제 해결과 로그
먼저 로그를 켜기
설정 문제를 살피기 전에 로그가 켜져 있는지 먼저 확인하세요. log 섹션 표기는 다음과 같습니다:
"log": {
"loglevel": "warning",
"access": "",
"error": ""
}
loglevel은 상세한 순서대로 debug, info, warning, error, none입니다. 문제를 볼 때 잠시 debug로 바꾸면 로그가 라우팅 매칭 결과, 연결 수립 과정 같은 세부를 찍어 주어 특정 규칙이 걸렸는지 확인할 수 있습니다. 문제가 해결되면 warning으로 되돌려 로그 파일이 빠르게 커지는 것을 막으세요. access와 error를 비워 두면 콘솔로 출력되고, GUI 클라이언트는 이 출력을 화면의 로그 패널로 리다이렉트합니다.
증상별 원인 찾기
| 증상 | 우선 확인 |
|---|---|
| 코어 시작 실패, 로그에 파싱 오류 | JSON 문법: 주석, 마지막 쉼표, 필드 이름 대소문자 |
| 시작은 되는데 브라우저에서 페이지가 열리지 않음 | 인바운드 포트와 시스템 프록시 설정이 일치하는지 |
| 로그에 수신 실패 표시 | 포트를 다른 프로그램이 점유하고 있는지 |
| 연결이 맺어진 직후 끊김 | 전송 파라미터가 서버와 항목별로 맞는지 |
| 일부 앱만 프록시를 탐 | 앱별 프록시 설정과 시스템 프록시 범위 |
| 도메인 분할이 적용되지 않음 | 스니핑이 켜져 있는지, 규칙 데이터 갱신이 필요한지 |
JSON 문법 자체 점검 순서
파싱에 실패하면 로그가 오류가 난 줄 번호를 알려 줍니다. 먼저 그 줄 근처에 전각 문장부호가 있는지 보세요. 전각 따옴표, 전각 쉼표, 전각 괄호는 웹 페이지에서 설정을 복사할 때 가장 흔히 생기는 문제입니다. 다음으로 마지막 항목 뒤 쉼표가 있는지 보고, 마지막으로 필드 이름의 철자와 대소문자를 확인하세요. 세 단계를 다 통과했는데도 오류가 나면 설정을 절반씩 주석 처리해 가며 다시 시도해서 이분법으로 해당 섹션을 좁히세요.
필드 이름과 버전
코어는 자기가 정의한 필드만 인식합니다. 남는 필드는 무시되거나 바로 오류가 나고, 같은 필드라도 코어 버전에 따라 처리가 다를 수 있습니다. 클라이언트를 업그레이드한 뒤 원래 잘 되던 설정에서 오류가 나면 파라미터를 반복해서 고치기보다 업데이트 안내를 보고 필드가 바뀌었는지 먼저 확인하세요. 손으로 설정을 쓸 때는 정상 동작하는 백업을 한 부 남겨 두면 문제가 생겼을 때 빠르게 되돌릴 수 있습니다.
실패 단계별로 로그 읽기
로그의 오류 메시지는 단계로 나눌 수 있습니다. 다이얼 단계 실패는 보통 주소, 포트, 전송 파라미터를 가리킵니다. 핸드셰이크 단계 실패는 보안 유형, 인증서, 공개키를 가리킵니다. 해석 단계 실패는 DNS 설정을 가리킵니다. 세 단계의 오류 메시지 형태가 다르므로 단계를 구분하면 시행착오를 많이 줄일 수 있습니다. debug 레벨 로그는 연결을 시도한 대상과 결과를 명확히 적어 주니 설정과 항목별로 대조하면 됩니다.
한 번에 파라미터 하나만 바꾸기
여러 파라미터를 동시에 바꾸고 테스트하면 문제가 해결되어도 어느 변경이 효과를 냈는지 알 수 없습니다. 한 번에 한 곳만 고치고 바로 검증하는 것이 설정 디버깅에서 시간을 가장 아끼는 방법입니다.
구독 갱신 실패는 설정과 무관
구독 갱신 실패는 설정 자체와 관계가 적고 구독 주소, 갱신 주기, 로컬 네트워크 상태 문제인 경우가 많습니다. 점검 순서는 구독 갱신 실패 점검에 항목별로 정리되어 있습니다. 구분 방법은 로그입니다. 설정 문제는 코어 시작 단계에서 오류가 나고, 구독 문제는 노드 목록 새로 고침에만 영향을 주며 이미 가져온 노드의 사용에는 영향을 주지 않습니다.
메인 라인으로 돌아가기
설정 파일의 동작은 결국 로그가 기준입니다. 이 페이지에서 다루지 않은 필드를 만나면 클라이언트에서 최소 설정에 하나씩 추가하며 로그 변화를 보는 편이, 설정을 한 번에 다 쓰고 디버깅하는 것보다 훨씬 빠릅니다. 기본 흐름을 다시 밟아야 할 때는 빠른 시작 튜토리얼로, 클라이언트 버전과 플랫폼 경로를 확인할 때는 클라이언트 받기로, 세 클라이언트의 차이를 알고 싶을 때는 비교 리뷰로 가세요.