JSON 매뉴얼 · 체계적으로 확인하기

V2Ray JSON 설정 파일
구조 및 필드 참고

최상위 객체부터 시작해 inbounds, outbounds, routing, dns, policy를 순서대로 확인합니다. 이 페이지에서는 필드의 의미, 참조 관계와 문제 해결 범위를 다룹니다. 구독을 처음 가져오거나 연결하는 방법은 먼저빠른 시작 튜토리얼을 확인하세요.

형식JSON 핵심 범위V2Fly / Xray 클라이언트v2rayN / v2rayNG / v2flyNG
01 · CONFIG

JSON 구조 개요와 확인 순서

먼저 코어 설정과 클라이언트 설정을 구분하세요

V2Ray 설정 파일은 하나의 JSON 객체입니다. 코어가 시작되면 최상위 필드를 읽은 뒤 인바운드 리스너, 아웃바운드 연결, 라우팅 선택, 도메인 해석, 연결 정책을 하나의 처리 흐름으로 구성합니다. 파일에서 필드가 나타나는 순서대로 한 줄씩 실행되는 방식이 아니므로 routinginbounds보다 앞에 작성해도 실행 결과는 달라지지 않습니다. 트래픽 방향을 실제로 결정하는 것은 필드 사이의 참조 관계이며, 특히 tag, inboundTag, outboundTag, balancerTag가 중요합니다.

v2rayN, v2rayNG, v2flyNG는 각각 고유한 UI 설정과 데이터 저장 방식을 사용합니다. 클라이언트에 표시되는 구독 그룹, 시스템 프록시 스위치, 앱별 설정이 코어 JSON에 그대로 존재하지 않을 수 있습니다. 데스크톱에서 문제를 해결할 때는 먼저 현재 실행에서 실제로 불러온 설정인지, 오래된 백업이나 구독 원문인지 확인하세요. 클라이언트가 설정을 다시 생성하면 실행 파일에 직접 입력한 내용이 교체될 수 있습니다. 장기간 유지할 규칙은 클라이언트가 제공하는 사용자 지정 라우팅, DNS 또는 고급 설정 메뉴에 입력하는 편이 좋습니다.

자주 사용하는 최상위 필드

필드 형식 역할 주요 연관 항목
log 객체 접속 로그, 오류 로그 및 로그 수준 정의 문제 원인 파악
inbounds 배열 로컬 앱 또는 네트워크 인터페이스의 연결 수신 routing.inboundTag
outbounds 배열 트래픽에 사용할 최종 연결 방식 정의 routing.outboundTag
routing 객체 도메인, 주소, 포트, 프로토콜 및 출처에 따라 아웃바운드 선택 인바운드 및 아웃바운드 태그
dns 객체 코어 내부의 도메인 해석 방식 정의 routing.domainStrategy
policy 객체 연결 시간 초과, 통계 활성화 및 시스템 수준 정책 정의 사용자 level, stats
stats 객체 코어 통계 모듈 활성화 policy의 통계 스위치

최소 설정이라고 해서 서버 객체 하나만 있으면 되는 것은 아닙니다. 완전한 처리에는 최소한 하나의 인바운드와 하나의 아웃바운드가 필요합니다. 인바운드는 앱이 요청을 코어에 전달하는 방식을, 아웃바운드는 코어가 요청을 처리하는 방식을 결정합니다. 라우팅, DNS, 정책은 잠시 생략할 수 있으며 이때 코어는 기본 동작을 사용합니다. 하지만 라우팅 규칙을 추가하는 순간 규칙에서 참조하는 모든 태그가 실제로 존재해야 합니다. 태그는 대소문자를 구분하므로 proxyProxy는 서로 다른 값으로 처리됩니다.

{
  "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"
    }
  ]
}

이 예제는 로컬 루프백 주소에서만 SOCKS를 수신하고, 들어온 트래픽을 모두 freedom 아웃바운드로 전달합니다. 코어가 JSON을 읽는지, 포트를 열 수 있는지, 앱이 프록시 포트를 올바르게 가리키는지 확인하는 데 적합하지만 원격 프록시 서버는 포함하지 않습니다. listen127.0.0.1을 사용하면 같은 네트워크의 다른 기기는 해당 포트에 연결할 수 없습니다. 모든 네트워크 인터페이스로 변경하기 전에는 접근 범위와 시스템 방화벽 규칙을 먼저 확인하세요.

JSON 문법의 경계

JSON의 객체 키와 문자열은 큰따옴표를 사용해야 하며, 배열이나 객체의 마지막 항목 뒤에는 쉼표를 남길 수 없습니다. 불리언 값은 true 또는 false만 사용할 수 있고 포트 번호는 따옴표로 감싸지 않습니다. 표준 JSON은 주석을 지원하지 않으므로 설명은 외부 문서나 클라이언트 메모 필드에 작성하고 실행 설정에 삽입하지 마세요. 예제를 복사할 때는 전각 문장 부호, 굽은 따옴표, 보이지 않는 공백도 확인해야 합니다. 서식 있는 텍스트 편집기가 이런 문자를 끼워 넣는 경우가 많습니다.

설정의 가독성은 주로 들여쓰기, 태그와 구간 나누기로 확보합니다. 두 칸 들여쓰기를 일관되게 사용하고, 태그는 local-socks, proxy, direct, block처럼 짧으면서 용도를 알 수 있는 영문으로 정하세요. 용도를 배열 위치에 의존하지 마세요. 나중에 아웃바운드를 추가하거나 라우팅을 조정할 때 안정적인 태그가 잘못된 참조를 줄여 줍니다. 목적이 구독을 가져와 바로 연결하는 것이라면 파일 전체를 직접 작성할 필요 없이V2Ray 사용 튜토리얼을 따라 하세요. 클라이언트를 선택하려면다운로드 센터로 이동하세요.

02 · INBOUNDS

inbounds 인바운드: 리스닝, 프로토콜 및 트래픽 진입점

인바운드 객체의 역할

inbounds는 배열이며 각 요소는 독립적인 진입점을 나타냅니다. 데스크톱 클라이언트에서 흔히 사용하는 진입점은 로컬 SOCKS와 HTTP 프록시 포트이며, 투명 프록시 환경에서는 dokodemo-door를 사용할 수도 있습니다. 요청이 코어에 들어오면 인바운드 태그, 대상 주소, 대상 포트, 네트워크 유형 등의 속성을 함께 전달하고, 이후 이 정보가 라우팅 모듈의 매칭에 사용됩니다. 인바운드는 최종적으로 프록시를 사용할지 직접 연결할지 차단할지를 결정하지 않고, 요청을 수신·해석하여 처리 흐름에 넘기는 역할만 합니다.

공통으로 사용하는 필드에는 tag, listen, port, protocol, settings, sniffing, streamSettings가 있습니다. 이 중 settings의 구조는 프로토콜에 따라 달라집니다. SOCKS에서는 인증 방식과 UDP를 설정할 수 있고, HTTP에서는 계정을 설정하며, dokodemo-door에서는 전달 대상을 설정할 수 있습니다. 서로 다른 프로토콜의 필드는 섞어 쓸 수 없습니다. SOCKS의 udp를 HTTP 인바운드에 넣어도 예상한 결과를 얻을 수 없습니다.

로컬 SOCKS 및 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": {}
    }
  ]
}

두 인바운드는 서로 다른 포트를 사용합니다. SOCKS를 지원하는 앱은 127.0.0.1:10808에 연결하고, HTTP 프록시만 지원하는 앱은 127.0.0.1:10809에 연결합니다. 같은 주소와 포트는 두 프로세스가 동시에 수신할 수 없습니다. 클라이언트 시작 후 포트 사용 중 오류가 표시되면 다른 클라이언트, 이전 코어 프로세스 또는 다른 프록시 도구가 실행 중인지 먼저 확인한 뒤 프로세스를 종료할지 포트를 변경할지 결정하세요.

auth: "noauth"는 루프백 주소에만 바인딩된 로컬 진입점에 적합합니다. 리스닝 범위를 로컬 네트워크로 넓힌다면 접근 제어를 애플리케이션 인증에만 의존하지 말고 시스템 방화벽으로 출처도 제한해야 합니다. 설정의 udp: true는 SOCKS 인바운드가 UDP 전달 요청을 받는다는 뜻이며, 모든 아웃바운드 프로토콜·전송 방식·상위 네트워크가 UDP 통신을 지원한다는 의미는 아닙니다. 웹페이지는 정상인데 음성, 게임 또는 도메인 해석에 문제가 생긴다면 인바운드, 라우팅, 아웃바운드, 상위 서버의 네 계층을 나누어 점검하세요.

sniffing과 대상 복원

일부 앱은 먼저 도메인을 주소로 해석한 뒤 주소를 프록시 포트에 제출합니다. 이 경우 라우팅 모듈이 보는 대상에는 주소만 남아 도메인 규칙을 직접 매칭할 수 없을 수 있습니다. sniffing은 조건에 맞는 연결에서 HTTP 호스트명이나 TLS 핸드셰이크의 서버 이름을 식별하여 라우팅 모듈이 도메인 정보를 활용하도록 합니다. 모든 트래픽을 식별 가능한 도메인으로 바꾸는 기능도 아니며, 범용 패킷 캡처 기능으로 이해해서도 안 됩니다.

destOverride는 식별을 허용할 유형을 지정합니다. 예제에서는 httptls를 활성화했습니다. routeOnly: true는 식별 결과를 주로 라우팅 판단에 사용하고 원래 연결 대상을 직접 바꾸지는 않는다는 뜻이므로 대상 변경에 따른 호환성 차이를 줄일 수 있습니다. 대상 식별을 켠 뒤 특정 앱의 연결에 문제가 생기면 먼저 SOCKS 인바운드에만 적용하고 앱별로 테스트하세요. 원인을 찾지 못한 상태에서 라우팅, DNS, 아웃바운드 전송을 동시에 변경하지 마세요.

필드 권장 점검 항목 흔한 증상
listen 로컬 사용 시 루프백 주소 우선 바인딩 잘못된 바인딩으로 앱이 연결하지 못하거나 노출 범위가 넓어짐
port 다른 프로세스가 사용 중인지 확인 코어 시작 실패, 클라이언트에 연결되지 않음으로 표시
protocol 앱이 실제로 지원하는 프록시 유형과 일치 SOCKS 포트로 HTTP 요청을 보내 즉시 실패
udp 아웃바운드와 상위 서버 기능을 함께 점검 TCP는 정상인데 UDP 서비스에 문제 발생
tag 라우팅의 inboundTag와 완전히 일치 인바운드 제한 규칙이 항상 매칭되지 않음

다중 진입점의 범위

다중 진입점은 앱의 출처를 나누는 데 적합합니다. 예를 들어 브라우저용 인바운드와 개발 도구용 인바운드를 각각 만들고, 라우팅 규칙에서 inboundTag로 서로 다른 아웃바운드를 가리킬 수 있습니다. 이렇게 하면 전역 규칙을 계속 바꾸는 것보다 구조가 명확합니다. 두 인바운드에 실제로 같은 정책이 필요하다면 형식적인 완전성을 위해 나눌 필요는 없습니다. 진입점이 많아질수록 포트 충돌, 시스템 프록시의 잘못된 대상 지정, 누락된 규칙이 발생할 가능성도 커집니다.

Android 클라이언트는 일반적으로 시스템 네트워크 인터페이스를 통해 앱 트래픽을 수신하며, 앱별 프록시와 우회 옵션이 실행 설정 생성에 참여합니다. 데스크톱 JSON의 로컬 리스닝 방식을 모바일에 그대로 적용할 수는 없습니다. v2rayNG는 Xray 코어를, v2flyNG는 v2fly 코어를 사용하므로 UI 필드와 코어에서 지원하는 기능이 다를 수 있습니다. 직접 조정해야 한다면 현재 클라이언트와 코어, 실제 설정 생성 위치를 먼저 확인한 뒤 필드를 대조하세요.

03 · OUTBOUNDS

outbounds 아웃바운드: 서버, 프로토콜 및 전송 계층

아웃바운드 객체의 3계층 구조

outbounds 역시 배열입니다. 각 아웃바운드는 보통 세 계층으로 구성됩니다. 공통 계층은 tagprotocol로 용도를 표시하고, 프로토콜 계층은 settings에 서버 주소·포트·사용자 매개변수를 입력하며, 전송 계층은 streamSettings에 TCP, WebSocket, gRPC, TLS 등을 설정합니다. 문제를 해결할 때는 이 세 계층을 나누어 확인해야 하며, “프로토콜이 맞다”는 사실을 “전체 연결이 정상이다”와 동일하게 보아서는 안 됩니다.

서버가 제공한 프로토콜, 주소, 포트, 사용자 식별자, 전송 방식, 보안 계층, 서버 이름은 하나의 묶음으로 확인해야 합니다. 프로토콜 이름만 바꾸거나 VMess 사용자 매개변수만 VLESS 사용자 매개변수로 교체한다고 설정이 변환되지는 않습니다. 구독 가져오기의 핵심 가치는 이러한 연관 필드를 완전한 하나의 노드로 클라이언트에 전달하는 데 있습니다. 직접 입력할 때는 이전 노드의 기본값에 의존하지 말고 서버 자료와 항목별로 대조하세요.

VLESS over TCP with TLS 예제

{
  "outbounds": [
    {
      "tag": "proxy",
      "protocol": "vless",
      "settings": {
        "vnext": [
          {
            "address": "server.example.com",
            "port": 443,
            "users": [
              {
                "id": "11111111-1111-4111-8111-111111111111",
                "encryption": "none"
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "tcp",
        "security": "tls",
        "tlsSettings": {
          "serverName": "server.example.com",
          "allowInsecure": false
        }
      }
    },
    {
      "tag": "direct",
      "protocol": "freedom"
    },
    {
      "tag": "block",
      "protocol": "blackhole"
    }
  ]
}

예제의 도메인과 사용자 식별자는 구조를 보여 주기 위한 값일 뿐이며, 실제 연결에는 서버에서 제공한 정보를 사용해야 합니다. address는 도메인이나 주소로 지정할 수 있는 연결 대상이고, port는 숫자입니다. VLESS 사용자 항목의 id는 신원 식별에 사용하며 encryption은 일반적으로 서버 요구 사항에 맞춥니다. 이는 외부 TLS와 다른 개념입니다. 전자는 프로토콜 사용자 매개변수이고 후자는 전송 보안 계층이므로 서로 대체할 수 없습니다.

network: "tcp"는 하위 전송이 TCP임을 의미합니다. security: "tls"는 TLS를 활성화하고, tlsSettings.serverName은 핸드셰이크에 사용할 서버 이름을 지정하며 일반적으로 서버 인증서와 배포 정보와 일치해야 합니다. allowInsecure: false로 정상적인 인증서 검증을 유지하세요. 연결에 실패했다고 먼저 true로 바꾸어 문제를 숨기지 말고 시스템 시간, 서버 이름, 주소 해석, 인증서 적용 범위, 중간 네트워크를 확인해야 합니다.

VMess 객체의 필드 위치

{
  "tag": "proxy-vmess",
  "protocol": "vmess",
  "settings": {
    "vnext": [
      {
        "address": "vmess.example.com",
        "port": 443,
        "users": [
          {
            "id": "22222222-2222-4222-8222-222222222222",
            "security": "auto"
          }
        ]
      }
    ]
  },
  "streamSettings": {
    "network": "ws",
    "security": "tls",
    "tlsSettings": {
      "serverName": "vmess.example.com"
    },
    "wsSettings": {
      "path": "/connection"
    }
  }
}

VMess와 VLESS는 모두 vnext를 사용할 수 있지만 users 내부 필드는 다릅니다. VMess 예제는 security를 사용하고 VLESS 예제는 encryption을 사용합니다. WebSocket 전송에는 wsSettings도 필요하며, 그 경로는 서버와 일치해야 합니다. 서버가 특정 요청 헤더를 요구한다면 해당 WebSocket 설정에 넣어야 합니다. TCP, WebSocket, gRPC는 각각 고유한 설정 객체를 사용하므로 이전 전송 방식의 불필요한 필드를 남겨 두지 마세요.

direct와 block은 처리 출구입니다

freedom 아웃바운드는 코어가 대상에 직접 연결하도록 하며 일반적으로 direct라는 태그를 붙입니다. blackhole은 매칭된 트래픽을 종료하며 보통 block으로 표시합니다. 이 두 아웃바운드는 원격 노드는 아니지만 라우팅 설정의 중요한 구성 요소입니다. 라우팅 규칙은 태그를 선택할 뿐이므로 같은 이름의 아웃바운드가 없으면 처리를 완료할 수 없습니다. 프록시, 직접 연결, 차단에 해당하는 세 태그를 명시해 규칙 의도가 파일에 드러나도록 하세요.

배열의 첫 번째 아웃바운드는 특별한 의미를 가집니다. 라우팅 규칙에 매칭되지 않으면 코어는 일반적으로 첫 번째 아웃바운드를 기본 출구로 사용합니다. 따라서 아웃바운드 순서를 이름순으로만 정해서는 안 됩니다. 미매칭 트래픽을 기본적으로 프록시로 보내려면 프록시 아웃바운드를 앞에 두고, 직접 연결을 기본값으로 삼으려면 direct를 앞에 두세요. 핵심 폴백 규칙도 명시적으로 작성하고 순서를 바꾼 뒤 실제 경로를 다시 확인하는 것이 안전합니다.

계층 대표 필드 확인할 출처
공통 계층 tagprotocol 로컬 이름과 프로토콜 유형
프로토콜 계층 addressportusers 서버 또는 구독 정보
전송 계층 networksecurity 서버 전송 설정
전송 세부 정보 tlsSettingswsSettings 서버 이름, 경로 등 배포 정보
04 · ROUTING

routing 라우팅: 매칭 조건, 규칙 순서 및 출구 선택

라우팅 규칙은 순서대로 처리됩니다

routing은 연결 속성에 따라 아웃바운드를 선택합니다. rules는 순서가 있는 배열이며 코어가 앞에서부터 확인하다가 처음 조건에 맞는 규칙의 출구를 사용하고 이후 규칙은 해당 연결의 선택에 참여하지 않습니다. 따라서 구체적인 규칙은 앞에, 범위가 넓은 규칙은 뒤에 배치해야 합니다. “모든 포트” 또는 “넓은 주소 범위” 규칙을 위에 두면 아래의 더 정밀한 도메인 규칙이 가려지는 경우가 많습니다.

하나의 규칙에 여러 조건을 넣으면 일반적으로 모든 조건을 동시에 만족해야 합니다. 예를 들어 inboundTag, domain, port를 함께 작성하면 진입점·도메인·포트가 모두 일치할 때만 매칭되며, 그중 하나만 맞아도 되는 것은 아닙니다. “도메인 조건 또는 주소 조건”을 표현하려면 같은 아웃바운드를 가리키는 두 규칙으로 나누는 편이 구조가 명확하고 로그에서도 매칭 경로를 확인하기 쉽습니다.

{
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        "type": "field",
        "inboundTag": [
          "local-socks"
        ],
        "domain": [
          "full:internal.example.com"
        ],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "ip": [
          "geoip:private"
        ],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "protocol": [
          "bittorrent"
        ],
        "outboundTag": "block"
      },
      {
        "type": "field",
        "network": "tcp,udp",
        "outboundTag": "proxy"
      }
    ]
  }
}

첫 번째 규칙은 local-socks로 들어오며 지정한 완전한 도메인을 대상으로 하는 요청만 처리합니다. 두 번째 규칙은 사설 주소 범위를 직접 연결하여 로컬 기기 트래픽이 원격으로 전송되지 않게 합니다. 세 번째 규칙은 식별 가능한 프로토콜에 따라 처리합니다. 마지막 규칙은 남은 TCP와 UDP 트래픽을 포괄하는 명시적 폴백입니다. 모든 outboundTagoutbounds에 존재해야 하며 태그 이름을 바꾸면 라우팅 참조도 함께 수정해야 합니다.

domainStrategy는 해석 시점을 결정합니다

domainStrategy는 라우팅 모듈이 도메인 대상에 대해 주소를 추가로 해석할지에 영향을 줍니다. AsIs는 주로 원래 도메인으로 매칭하며 주소 규칙을 위해 적극적으로 해석하지 않습니다. IPIfNonMatch는 먼저 도메인 규칙을 시도하고 매칭되지 않을 때 주소를 해석한 뒤 주소 규칙을 다시 확인합니다. IPOnDemand는 규칙 판단에 주소가 필요할 때 더 일찍 해석을 시작합니다. 전략이 적극적일수록 주소 규칙이 참여하기 쉽지만 DNS 동작이 라우팅 결과에 직접 미치는 영향도 커집니다.

설정이 주로 도메인 분류에 의존하고 대상 주소로 판단할 필요가 없다면 AsIs부터 시작할 수 있습니다. geoip나 사용자 지정 대역을 함께 사용한다면 IPIfNonMatch가 보통 이해하기 쉽습니다. 먼저 도메인을 확인하고 매칭되지 않을 때 해석하기 때문입니다. 이 필드를 조정한 뒤 경로가 달라지면 라우팅 배열만 보지 말고 DNS 서버 선택, 해석 결과, 캐시도 함께 점검하세요.

도메인 매칭 방식

방식 의미 적용 상황
full:example.com 완전한 도메인만 매칭 하나의 고정 호스트
domain:example.com 해당 도메인과 일반적인 하위 도메인 범위 매칭 사이트 도메인 단위로 규칙 구성
regexp:^.+\.example\.com$ 정규식으로 매칭 구조가 복잡해 앞의 두 방식으로 표현할 수 없을 때
geosite:category-ads-all 코어에서 사용할 수 있는 도메인 분류 데이터 참조 클라이언트와 코어에 해당 데이터가 준비되어 있을 때

정규식은 유연하지만 유지 관리 비용이 높습니다. 마침표 같은 문자는 정규식 규칙에 따라 이스케이프해야 하고, JSON 문자열에 넣을 때는 백슬래시도 JSON 이스케이프 규칙을 따라야 합니다. full: 또는 domain:으로 표현할 수 있다면 더 직관적인 방식을 우선하세요. 분류 데이터 규칙은 클라이언트가 코어와 함께 제공하는 데이터 파일에 의존합니다. 같은 규칙이 기기마다 다르게 동작한다면 분류 내용이 완전히 같다고 가정하지 말고 데이터 파일의 출처와 업데이트 시점을 확인하세요.

주소, 포트, 출처 및 네트워크 유형

ip에는 단일 주소, CIDR 대역 또는 코어가 지원하는 주소 분류를 입력할 수 있습니다. port에는 단일 포트, 범위 또는 쉼표로 구분한 조합을 입력할 수 있습니다. 예: 53, 80-443. sourcesourcePort는 출처 정보로 규칙을 제한하고, network는 TCP와 UDP를 구분하며, inboundTag는 진입점에 따라 앱 경로를 나눕니다. 이러한 조건은 실제 요구에 맞게 조합하고 필드를 빠짐없이 채우기 위해 모두 작성하지는 마세요.

포트만으로 판단하면 대상 포트만 알 수 있을 뿐 앱이나 프로토콜을 안정적으로 식별할 수 없습니다. 웹 서비스가 비표준 포트를 사용할 수도 있고 다른 서비스가 흔한 포트를 사용할 수도 있습니다. 대상을 안정적으로 식별하려면 도메인, 주소, 인바운드 출처를 함께 사용하는 편이 좋습니다. 프로토콜 식별은 코어가 해당 트래픽을 식별할 수 있는지에 달려 있으며 식별하지 못하면 규칙은 매칭되지 않습니다. 라우팅에는 명확한 폴백을 남겨 식별되지 않은 연결이 예상치 못한 출구로 빠지지 않게 하세요.

복잡한 분할 라우팅은 세 규칙부터 시작하는 것이 좋습니다. 사설 주소는 직접 연결하고, 명확히 처리해야 하는 대상은 지정 출구로 보내며, 나머지는 기본 출구를 사용합니다. 기본 경로가 안정된 것을 확인한 뒤 도메인 분류, 포트, 출처 조건을 단계적으로 추가하세요. 많은 규칙을 한꺼번에 가져오면 충돌 위치를 확인하기 어렵습니다. 자주 쓰는 분할 라우팅 용어는용어 매뉴얼에서 확인할 수 있습니다. 규칙이 이미 매칭되었는데도 연결이 실패한다면 매칭 순서를 반복해서 바꾸지 말고 아웃바운드와 DNS를 계속 점검하세요.

05 · DNS

dns 설정: 해석 서버, 도메인 그룹 및 라우팅 연동

코어 DNS와 시스템 DNS의 범위

dns는 코어 내부에서 도메인을 해석할 때 사용할 서버와 매칭 방식을 정의합니다. 운영체제의 모든 DNS 설정과 같은 의미는 아닙니다. 앱이 시스템 계층에서 먼저 해석한 뒤 주소를 프록시에 전달할 수도 있고, 앱이 프록시를 통해 도메인을 제출하여 코어나 원격 측에서 처리할 수도 있습니다. 특정 DNS 규칙이 요청에 참여하는지 판단하려면 도메인이 어느 계층에서 해석되는지, 인바운드 대상 식별이 도메인 정보를 제공하는지 먼저 확인해야 합니다.

라우팅의 domainStrategy가 코어 해석을 트리거할 수 있고, 아웃바운드 연결의 서버 주소도 해석이 필요하며, 일부 DNS 조회는 일반 트래픽처럼 다시 라우팅을 거칠 수 있습니다. 설정이 잘못되면 순환 의존성이 생기기 쉽습니다. 코어가 프록시 서버에 연결하려면 해당 도메인을 해석해야 하는데, 해석 요청이 아직 연결되지 않은 프록시 아웃바운드로 라우팅되는 경우입니다. 시작 과정에 필요한 대상이 명확하고 도달 가능한 해석 경로를 갖도록 구성하고 규칙은 단순하게 유지하세요.

{
  "dns": {
    "hosts": {
      "router.example": "192.168.1.1"
    },
    "servers": [
      {
        "address": "223.5.5.5",
        "domains": [
          "domain:example.cn"
        ],
        "expectIPs": [
          "geoip:cn"
        ]
      },
      {
        "address": "1.1.1.1",
        "domains": [
          "domain:example.com"
        ]
      },
      "localhost"
    ],
    "queryStrategy": "UseIP"
  }
}

hosts는 정적 매핑을 제공하며 고정된 로컬 서비스 이름이나 명확히 덮어써야 하는 소수의 도메인에 적합합니다. 대규모 도메인 목록을 대신하는 기능은 아닙니다. 정적 매핑은 실제 서비스 변경을 자동으로 따라가지 않으므로 관리 가능한 대상만 설정해야 합니다. 예제에서는 구조를 설명하기 위해 router.example을 사설 주소에 연결했습니다. 실제 설정에는 로컬 네트워크 정보를 입력하세요.

servers에는 해석 서버를 순서대로 나열합니다. 객체 형식에는 domains를 추가해 특정 도메인을 해당 서버에 우선 전달할 수 있고, 문자열 형식은 일반 서버나 폴백으로 사용할 수 있습니다. localhost는 로컬 해석 기능을 사용한다는 뜻입니다. 클라이언트가 DNS를 관리하는 경우 코어 설정이 클라이언트에 의해 다시 작성될 수도 있으므로 최종 동작은 실행 시 설정과 로그를 기준으로 판단해야 합니다.

domains와 expectIPs

domains는 어떤 도메인을 현재 서버에서 우선 처리할지 결정하며 라우팅 도메인 규칙과 비슷한 형식을 사용합니다. 정확한 호스트에는 full:, 전체 도메인 범위에는 domain:, 분류 데이터에는 해당 분류 식별자를 사용할 수 있습니다. 서버 객체에 domains가 없으면 일반 후보로 동작하는 경우가 많습니다. 여러 서버가 모두 조건에 맞을 때의 세부 동작은 코어 구현과 조회 전략의 영향도 받으므로 중복 범위를 줄이세요.

expectIPs는 해석 결과에 적용할 예상 주소 필터입니다. 예제는 첫 번째 서버 그룹이 지정한 주소 분류에 맞는 주소를 반환하도록 요구하며, 결과가 조건에 맞지 않으면 코어가 다른 후보를 계속 고려할 수 있습니다. 이는 “이 도메인 그룹은 특정 유형의 주소로 해석되어야 한다”는 제약을 표현하는 데 적합하지만, 상위 서버에 연결할 수 없거나 서버 응답이 시간 초과되거나 분류 데이터가 없다는 문제를 해결하지는 못합니다. 문제를 해결할 때는 먼저 이 제한을 잠시 제거해 기본 조회가 성공하는지 확인한 뒤 결과 제약을 복원하세요.

queryStrategy와 주소 체계

전략 주요 동작 사용 전 확인
UseIP 사용 가능한 주소 유형을 조회 시스템과 아웃바운드가 반환 주소를 모두 처리할 수 있는지
UseIPv4 IPv4 조회 결과를 우선 대상이 IPv4 주소를 제공하는지
UseIPv6 IPv6 조회 결과를 우선 로컬 네트워크와 아웃바운드에 IPv6 연결성이 있는지

주소 체계 선택은 실제 네트워크 성능과 일치해야 합니다. IPv6 주소가 해석되었다고 해서 현재 기기, 라우터, 프록시 서버, 아웃바운드 경로 전체가 해당 주소에 도달할 수 있다는 뜻은 아닙니다. 일부 도메인만 오래 대기하거나 네트워크를 바꾸면 정상화된다면 반환된 주소 유형과 아웃바운드 연결성을 먼저 확인하세요. 반대로 IPv4를 강제하면 IPv6 주소만 제공하는 대상은 해석되지 않을 수 있습니다. 전략은 범용 가속 스위치가 아니라 실제 네트워크 상황을 기준으로 정해야 합니다.

DNS 요청은 라우팅을 어떻게 거치는가

DNS 서버에 주소를 입력하면 조회 대상이 바로 아웃바운드 선택으로 들어갈 수 있지만, 도메인을 입력하면 DNS 서버 자체의 도메인부터 해석해야 합니다. 특정 조회 그룹을 프록시로 보내려면 순환이 생기지 않으면서 식별 가능한 DNS 대상 라우팅 규칙을 작성해야 합니다. 가장 간단한 점검 방법은 두 경로를 그려 보는 것입니다. 업무 도메인은 누가 해석하는지, DNS 서버 자체는 누가 해석하는지 확인하세요. 어느 한 경로라도 아직 연결되지 않은 자기 자신으로 되돌아가면 구성을 다시 설계해야 합니다.

도메인 규칙이 적용되지 않을 때는 먼저 앱이 코어에 도메인과 주소 중 무엇을 전달하는지 확인하세요. 주소만 전달된다면 SOCKS 요청 방식, 대상 식별, 라우팅의 domainStrategy를 점검할 수 있습니다. DNS 결과는 올바른데 연결 대상이 잘못되면 캐시, 정적 매핑, 클라이언트가 생성한 추가 규칙을 확인하세요. 해석 서버 교체, 주소 체계 변경, 라우팅 순서 변경을 동시에 수행하지 마세요. 문제가 해결되어도 실제 원인을 알 수 없게 됩니다.

중국 본토와 해외 도메인 그룹, 원격 해석, 주소 규칙을 조합하는 방법을 더 알아보려면 블로그의V2Ray DNS 설정 상세 가이드를 읽어 보세요. 해당 글은 설계 방식에 초점을 두고, 이 장에서는 필드와 실행 범위를 다룹니다. 클라이언트 UI 옵션과 직접 작성한 필드가 다를 때는 현재 클라이언트가 사용하는 코어의 지원 범위와 실제 생성된 설정을 기준으로 판단하세요.

06 · POLICY

policy 정책: 시간 초과, 사용자 레벨 및 통계 스위치

policy는 아웃바운드를 선택하지 않습니다

policy는 연결 수명과 통계 스위치를 정의하며 도메인이나 주소에 따른 트래픽 분할은 담당하지 않습니다. 일반적인 구조에는 levelssystem이 포함됩니다. levels는 사용자 레벨을 키로 사용해 같은 레벨의 사용자에게 핸드셰이크, 유휴, 업·다운링크 유지 시간과 통계 항목을 설정합니다. system은 인바운드와 아웃바운드의 시스템 수준 통계를 제어합니다. 특정 도메인의 출구를 바꾸려면 routing을 수정해야 하며 정책에서 규칙 필드를 찾으면 안 됩니다.

레벨은 속도 등급도 우선순위도 아닙니다. 프로토콜 사용자 객체는 level로 같은 이름의 정책 그룹을 참조할 수 있으며, 명시하지 않으면 일반적으로 기본 레벨을 사용합니다. 여러 레벨은 서로 다른 연결 수명이 실제로 필요할 때만 의미가 있습니다. 일반적인 클라이언트 설정에는 레벨 하나만 두어 사용자 객체와 정책 객체 사이의 참조 복잡성을 줄이는 편이 좋습니다.

{
  "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는 연결을 설정하는 단계에서 기다릴 수 있는 시간을 제어합니다. 값이 너무 작으면 지연이 큰 네트워크나 느린 핸드셰이크가 일찍 실패하고, 너무 크면 실제로 도달할 수 없는 연결을 더 오래 기다리게 됩니다. 웹페이지 전체 로딩 시간 초과나 앱 자체 요청 기한을 결정하는 값은 아닙니다. 핸드셰이크가 실패하면 먼저 주소, 포트, 전송 계층, 네트워크 연결성을 확인한 뒤 이 값을 조정할 필요가 있는지 판단하세요.

connIdle은 데이터 활동이 없을 때 연결을 유지할 수 있는 시간을 의미합니다. 너무 짧으면 장시간 연결, 푸시 메시지, 간헐적 전송에 영향을 줄 수 있고 너무 길면 사용하지 않는 연결이 자원을 더 오래 차지합니다. 적절한 값은 업무 유형에 따라 달라지며 모든 네트워크에 맞는 단일 값은 없습니다. 모바일 네트워크가 자주 전환되면 코어에 이전 연결이 남아 있어도 더 이상 사용할 수 없을 수 있으므로 클라이언트의 재연결 동작도 함께 판단해야 합니다.

uplinkOnlydownlinkOnly는 반쯤 닫힌 상태에서 연결을 유지할 시간을 설정합니다. 한쪽 데이터 방향이 끝난 뒤에도 다른 쪽에서 잠시 전송이 필요할 수 있습니다. 너무 일찍 종료하면 끝부분 데이터가 잘리고, 너무 오래 유지하면 자원 해제가 늦어집니다. 일반적인 사용에서는 이 두 필드를 먼저 조정하지 마세요. 로그와 실제 동작이 반폐쇄 처리 문제를 명확히 가리킬 때만 하나씩 테스트하는 것이 좋습니다.

통계는 두 계층을 모두 활성화해야 합니다

statsUserUplinkstatsUserDownlink는 사용자 수준 통계를 제어하고, system 아래 필드는 인바운드와 아웃바운드 통계를 제어합니다. 통계 데이터가 실제로 생성되려면 일반적으로 최상위에 stats 객체도 있어야 하며 클라이언트나 API가 이를 읽어야 합니다. 정책 스위치만 켠다고 보이는 차트가 자동으로 생성되거나 결과가 특정 페이지에 자동 저장되지는 않습니다.

{
  "stats": {},
  "policy": {
    "system": {
      "statsInboundUplink": true,
      "statsInboundDownlink": true,
      "statsOutboundUplink": true,
      "statsOutboundDownlink": true
    }
  }
}

통계를 활성화하면 처리량과 메모리 사용량이 어느 정도 늘어납니다. 문제 해결을 위해 일시적으로 인바운드와 아웃바운드 트래픽을 확인하는 경우 필요한 범위만 켰다가 완료 후 원래 범위로 되돌리세요. 사용자 수준 통계는 프로토콜 사용자에게 해당 레벨과 식별자가 있어야 하며 서버 관리나 명확한 데이터 수집이 필요한 상황에 적합합니다. 일반적인 로컬 클라이언트에서는 사용자별 통계 계층보다 아웃바운드에 트래픽이 발생하는지를 확인하는 일이 더 중요합니다.

필드 단위 또는 유형 조정 시 위험
handshake 너무 짧으면 연결 설정 단계가 조기에 종료됨
connIdle 너무 짧으면 장시간 연결에 영향, 너무 길면 자원 해제가 지연됨
uplinkOnly 업링크 종료 후 연결 유지에 영향
downlinkOnly 다운링크 종료 후 연결 유지에 영향
statsInboundUplink 불리언 값 활성화하면 해당 통계 데이터와 추가 비용이 발생함

정책 변경 테스트 방법

연결 정책을 조정할 때는 노드, 라우팅, DNS를 고정하고 정책 값 하나만 변경해야 합니다. 테스트 대상도 명확히 하세요. 핸드셰이크 문제는 새 연결을 관찰하고, 유휴 문제는 연결을 만든 뒤 기다리며, 반폐쇄 문제는 한 방향의 종료를 재현할 수 있는 업무로 확인해야 합니다. 웹페이지를 한 번 새로 고치는 것만으로는 모든 필드를 검증할 수 없습니다. 테스트가 끝나면 원래 값, 변경 값, 현상을 기록해 여러 차례 조정한 뒤에도 기준을 잃지 않도록 하세요.

클라이언트는 시작할 때 기본 policy를 생성할 수도 있고, 아예 생략한 채 코어 기본값을 사용할 수도 있습니다. 필드를 생략했다고 값이 0이라는 뜻은 아닙니다. 현재 연결이 안정적이고 통계나 수명 관리에 대한 명확한 요구가 없다면 출처가 불분명한 매개변수 묶음을 복사하기보다 기본 동작을 유지하는 편이 좋습니다. v2rayN, v2rayNG, v2flyNG의 플랫폼별 용도를 비교하려면비교 평가로 이동하세요. 정책 필드 지원 여부는 각 코어와 클라이언트의 구현을 기준으로 판단해야 합니다.

07 · ASSEMBLY

전체 조합, 로드 확인 및 계층별 문제 해결

각 구간을 추적 가능한 처리 흐름으로 조합하기

완전한 설정에서 중요한 것은 필드 수가 아니라 모든 연결이 명확한 경로를 따라 이동하는지입니다. 앱이 인바운드에 연결하고, 인바운드가 대상 정보를 만들며, 필요할 때 DNS가 해석 결과를 제공하고, 라우팅이 아웃바운드를 선택한 뒤, 아웃바운드가 프로토콜과 전송 설정에 따라 연결을 수립하고, policy가 수명을 관리합니다. 어느 한 계층에서 오류가 나도 클라이언트에는 “연결할 수 없음”으로 나타날 수 있습니다. 문제를 해결할 때는 막연한 증상을 구체적인 단계로 나누어야 합니다.

{
  "log": {
    "loglevel": "warning"
  },
  "dns": {
    "servers": [
      "localhost"
    ],
    "queryStrategy": "UseIP"
  },
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        "type": "field",
        "ip": [
          "geoip:private"
        ],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "network": "tcp,udp",
        "outboundTag": "proxy"
      }
    ]
  },
  "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": "server.example.com",
            "port": 443,
            "users": [
              {
                "id": "11111111-1111-4111-8111-111111111111",
                "encryption": "none"
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "tcp",
        "security": "tls",
        "tlsSettings": {
          "serverName": "server.example.com",
          "allowInsecure": false
        }
      }
    },
    {
      "tag": "direct",
      "protocol": "freedom"
    },
    {
      "tag": "block",
      "protocol": "blackhole"
    }
  ],
  "policy": {
    "levels": {
      "0": {
        "handshake": 4,
        "connIdle": 300
      }
    }
  }
}

이 예제는 구조 관계를 보여 주며 서버 정보는 실제 자료로 교체해야 합니다. 사설 주소는 먼저 direct에 매칭되고 나머지 TCP와 UDP 트래픽은 proxy로 전달됩니다. SOCKS 인바운드는 로컬에만 바인딩하고 DNS는 시스템 해석 기능을 사용하며, 정책은 기본 연결 수명만 설정합니다. 설정에 block을 정의했지만 현재 이를 참조하는 라우팅 규칙은 없습니다. 이후 명확한 차단 규칙을 추가할 수 있도록 이 출구를 남겨 둡니다.

첫 번째 계층: 파일을 읽을 수 있는가

먼저 파일 인코딩, JSON 문법, 키 이름과 데이터 유형을 확인하세요. 흔한 오류로는 쉼표 누락, 마지막 항목 뒤의 불필요한 쉼표, 작은따옴표 사용, 문자열로 작성한 포트, 배열 바깥의 대괄호 한 겹 누락, 복사 과정에서 들어간 전각 문장 부호가 있습니다. 코어가 시작 단계에서 바로 종료되면 오류 로그의 필드 경로와 행·열 위치를 먼저 확인하세요. 문법이 통과하기 전에는 라우팅이 매칭되는지 논의하지 마세요.

그래픽 클라이언트 사용자는 파일이 현재 프로세스에 실제로 로드되는지도 확인해야 합니다. v2rayN은 현재 서버, 라우팅, DNS 설정에 따라 코어 설정을 다시 생성할 수 있으며 v2rayNG와 v2flyNG도 모바일 UI 상태에 따라 실행 매개변수를 생성합니다. 사용되지 않는 내보내기 파일을 수정해도 현재 연결은 바뀌지 않습니다. 클라이언트 로그, 설정 미리보기, 시작 매개변수로 실제 로드 경로를 확인할 수 있습니다.

두 번째 계층: 인바운드가 생성되었는가

코어가 시작된 뒤 로컬 리스닝 주소와 포트가 실제로 존재하는지 확인하세요. 앱의 프록시 유형은 인바운드 프로토콜과 일치해야 합니다. SOCKS 앱은 SOCKS 포트를, HTTP 앱은 HTTP 포트를 가리켜야 합니다. 시스템 프록시가 켜져 있는데도 브라우저가 직접 연결한다면 시스템 프록시 대상, 브라우저의 별도 프록시 설정, 현재 클라이언트 모드를 점검하세요. 앱이 즉시 프록시 서버 연결 거부를 표시하면 코어 실행 여부, 포트 일치 여부, 로컬 방화벽부터 확인하는 것이 일반적입니다.

특정 앱에서만 문제가 생겼다고 즉시 노드가 잘못되었다고 판단하지 마세요. 해당 앱이 시스템 프록시를 읽지 않거나 자체 DNS, HTTP/3, 독립 네트워크 설정을 사용할 수 있습니다. 먼저 SOCKS 또는 HTTP 프록시를 확실히 지원하는 테스트 앱으로 인바운드를 확인한 다음 대상 앱의 프록시 기능을 점검하세요. 모바일에서는 앱별 적용 범위와 시스템 네트워크 인터페이스가 설정되었는지도 확인합니다.

세 번째 계층: 라우팅과 DNS가 예상 대상을 제공하는가

인바운드가 연결을 받은 뒤 대상이 도메인인지 주소인지 확인하고, 라우팅에서 처음 매칭된 규칙을 점검하세요. 로그에 잘못된 아웃바운드가 표시되면 규칙 순서, 태그 철자, 조건 조합을 중점적으로 확인합니다. 도메인 규칙은 매칭되지 않고 주소 규칙만 적용된다면 앱의 해석 위치, sniffing, domainStrategy를 점검하세요. 해석이 시간 초과되면 DNS 서버와 결과 제한을 잠시 줄여 기본 조회 경로를 확인합니다.

라우팅 문제와 DNS 문제는 서로 영향을 주는 경우가 많습니다. 다음 두 질문을 분리해 확인해야 합니다. 대상 도메인이 무엇으로 해석되었는가, 해석이 끝난 뒤 어떤 규칙이 매칭되었는가. 이 두 단계를 나누어야 해석 결과가 예상과 다른지, 규칙 순서가 잘못되었는지 판단할 수 있습니다. 노드를 계속 교체하는 것만으로는 로컬 규칙 충돌을 해결할 수 없습니다.

네 번째 계층: 아웃바운드가 연결을 완료할 수 있는가

아웃바운드 단계는 정해진 순서로 확인하세요. 서버 주소 해석, 포트 연결 가능 여부, 프로토콜 유형, 사용자 매개변수, 전송 유형, 보안 계층, 서버 이름, 경로 또는 서비스 이름 순입니다. 로그의 시간 초과는 대개 네트워크 연결성이나 경로 문제를 가리키고, 즉시 끊기는 현상은 포트·프로토콜·핸드셰이크 매개변수 불일치에서 흔히 발생합니다. 시스템 시간이 크게 틀려도 TLS 연결에 영향을 줄 수 있습니다. 매번 한 필드만 바꾸고 복원할 수 있는 원래 설정을 보관하세요.

증상 우선 확인 다음 단계
코어 시작 후 즉시 종료 JSON 문법, 필드 유형, 포트 사용 여부 오류 로그에서 필드 경로 확인
앱이 로컬 프록시에 연결하지 못함 인바운드 주소, 포트, 프로토콜 유형 앱의 프록시 설정과 코어 프로세스 확인
일부 도메인이 잘못된 출구로 이동 규칙 순서, 대상 유형, DNS 결과 처음 매칭된 규칙 기록
모든 원격 연결이 시간 초과 서버 해석, 포트, 네트워크 경로 이어서 전송 계층과 보안 계층 확인
TCP는 정상인데 UDP 이상 인바운드 UDP, 라우팅 네트워크 유형, 아웃바운드 기능 상위 서버와 앱 동작 확인
수정 후 다시 시작해도 변화 없음 실제로 로드된 파일, 클라이언트 자동 생성 여부 실행 설정에서 설정 출처 역추적

로그 수준과 최소 재현

일상적인 사용에서는 로그 수준을 비교적 간결하게 유지해도 됩니다. 문제를 찾을 때만 로그 정보를 잠시 늘리고, 명확한 작업을 한 번 재현한 뒤 원래 설정으로 되돌리세요. 로그가 많다고 결론이 빨리 나오는 것은 아닙니다. 시간, 인바운드 태그, 대상, 라우팅 결과, 아웃바운드 오류를 중심으로 읽어야 합니다. 로그를 공유하기 전에는 서버 주소, 사용자 식별자, 구독 내용, 로컬 경로 등 민감한 정보를 처리하세요.

최소 재현 설정에는 인바운드 하나, 프록시 아웃바운드 하나, 직접 연결 아웃바운드 하나, 최소한의 규칙만 남깁니다. 최소 설정으로 연결되면 DNS 그룹, 추가 인바운드, 복잡한 라우팅과 정책을 단계적으로 추가하세요. 문제가 다시 나타난 단계의 신규 내용에 원인이 있을 가능성이 큽니다. 최소 설정에서도 실패한다면 서버 매개변수, 네트워크 경로, 코어 호환성으로 범위를 좁히세요. 완전한 설정에서 무작위로 필드를 삭제하는 것보다 안정적인 방법입니다.

그래도 판단하기 어렵다면FAQ 문제 해결에서 증상별로 계속 찾아보세요. 클라이언트를 다시 설치하거나 플랫폼별 대응 관계를 확인하려면다운로드 센터에서 v2rayN, v2rayNG, v2flyNG를 선택하세요. 데스크톱에서는 v2rayN을 우선 사용하고 Android에서는 필요한 코어에 따라 v2rayNG 또는 v2flyNG를 선택할 수 있습니다. 재설치하기 전에 현재 구독, 라우팅, DNS 설정을 기록해 설정 문제와 프로그램 문제를 한 번에 섞지 않도록 하세요.