CONFIGURATION REFERENCE

V2Ray JSON 설정 가이드

설정 구조와 필드 범위, 단계별 검증 방법을 안내합니다.

이 페이지는 설정 파일을 읽거나 수정하거나 문제를 해결하려는 사용자를 위한 안내입니다. 처음 연결한다면 입문 가이드에 따라 구독을 가져오고 연결을 확인한 뒤, 이 페이지에서 필요한 필드를 살펴보세요. 데스크톱에서는 v2rayN을, Android에서는 v2rayNG를 사용하거나 코어에 따라 v2flyNG를 선택할 수 있습니다. 설치 파일은 클라이언트 다운로드 페이지에서 확인하세요.

읽기 전에: 예제는 일반적인 기존 JSON 설정 구조를 사용합니다. 각 필드의 지원 여부는 실제로 실행 중인 V2Fly 또는 Xray 코어와 해당 문서를 기준으로 확인하세요. 각 코드 조각은 특정 계층을 설명하기 위한 것으로, 서로 다른 절의 객체를 그대로 이어 붙여서는 안 됩니다.

1. JSON 구조와 설정 적용 범위

클라이언트와 코어를 구분하세요

v2rayN, v2rayNG, v2flyNG는 설정을 관리하는 GUI 클라이언트입니다. 실제로 연결을 받고 규칙을 적용해 데이터를 전달하는 것은 코어입니다. 클라이언트 화면의 구독, 설정 목록, 시스템 프록시, 라우팅 프리셋이 설정 파일의 특정 객체와 항상 일대일로 대응하는 것은 아닙니다. JSON을 살펴보기 전에 클라이언트의 실행 정보나 시작 로그에서 코어 이름을 확인하고, 해당 항목이 자동 생성 설정을 사용하는지 사용자 지정 파일을 사용하는지 파악하세요.

기존 설정의 최상위에는 보통 객체 하나가 있으며, 그 안은 역할에 따라 log, inbounds, outbounds, routing, dns, policy로 나뉩니다. 인바운드와 아웃바운드는 배열입니다. 하나의 프로세스가 여러 로컬 연결 지점을 제공하거나 여러 전달 경로를 선택할 수 있기 때문입니다. 배열에 항목이 하나뿐이더라도 대괄호를 중괄호로 바꾸지 마세요.

최소 설정으로 기준 상태 만들기

아래는 로컬 SOCKS 인바운드에서 직접 연결 아웃바운드로 전달하는 기본 설정입니다. 코어 시작, 로컬 포트 수신, 앱 연결 여부를 확인할 수 있지만 원격 프록시 연결은 만들지 않습니다. 테스트할 때 앱이 로컬 SOCKS 포트를 명시적으로 사용하도록 설정하고, 직접 접속이 허용된 대상을 선택하세요. 기본 연결이 확인된 뒤 원격 아웃바운드, 라우팅, DNS를 추가해야 새 필드가 어떤 영향을 주는지 판단할 수 있습니다.

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

JSON의 문자열과 키 이름은 큰따옴표로 묶고, 불리언 값은 true 또는 false로 씁니다. 포트 번호는 단위를 붙인 문자열이 아니라 숫자로 입력하세요. 표준 JSON은 주석을 지원하지 않으며 마지막 항목 뒤에 쉼표를 둘 수 없습니다. 일부 코어의 파서는 확장 문법을 허용할 수 있지만, 클라이언트 가져오기나 구독 변환 등 다른 도구를 거치면 엄격한 JSON으로 파싱될 수 있습니다. 직접 작성하는 파일은 표준 형식을 유지하는 편이 안전합니다.

최상위 객체에서 같은 키를 중복해 쓰는 것도 놓치기 쉬운 문제입니다. 일부 파서는 마지막 값만 남기고, 일부 도구는 파일을 읽지 못합니다. 어느 쪽이든 설정을 합치는 방법으로 적절하지 않습니다. 예를 들어 routing 객체를 두 개 작성해도 규칙이 자동으로 합쳐지지 않습니다. 규칙을 추가할 때는 하나의 rules 배열을 수정하고 새 규칙을 넣을 위치를 명확히 정하세요.

설정을 수정하는 위치에 따라 변경 내용의 유지 여부가 달라집니다

자동 생성 설정은 현재 노드, 라우팅 프리셋, 설정값을 바탕으로 클라이언트가 다시 구성하는 경우가 많습니다. 실행 폴더의 임시 파일을 직접 수정하면 현재 실행 중에만 반영될 수 있으며, 노드 전환, 구독 갱신, 서비스 재시작 후 파일이 덮어써질 수 있습니다. 계속 사용할 설정은 클라이언트가 지원하는 사용자 지정 항목에 입력하거나 별도 파일로 저장한 다음, 해당 사용자 지정 설정 기능으로 불러오세요.

사용자 지정 설정에도 적용 범위가 있습니다. 클라이언트 화면의 로컬 포트, 라우팅 모드, DNS 옵션이 사용자 지정 파일의 필드보다 우선한다고 볼 수는 없습니다. 반대로 일부 GUI 기능은 주변 실행 매개변수를 계속 생성할 수 있습니다. 설정 페이지의 체크 여부가 아니라 코어가 최종적으로 읽는 설정과 로그를 기준으로 판단하세요. 설정 모드를 처음 전환할 때는 기존 연결을 중지하고 설정 위치를 기록한 다음, 다시 시작해 적용 여부를 확인하세요.

주요 설정 객체의 역할
객체주요 역할담당하지 않는 항목
inbounds앱 연결 수신브라우저 프록시 자동 설정
outbounds출구와 원격 프로토콜 정의전체 라우팅 순서 결정
routing연결에 사용할 출구 선택시스템 DNS 설정 변경
dns코어의 도메인 이름 확인 설정 제공모든 앱의 DNS 조회 자동 가로채기

코어마다 일부 필드 이름이 같다고 해서 전체 설정을 그대로 서로 바꿔 쓸 수 있는 것은 아닙니다. 확장 프로토콜, 전송 방식, DNS 옵션, 정책 기본값이 다를 수 있습니다. 다른 코어로 옮길 때는 인바운드, 직접 연결, 차단 등 기본 구성을 먼저 유지하고 대상 코어가 지원하는 기능을 하나씩 추가하세요. 파싱 오류는 우선 필드 경로를 기준으로 해결하고, 코어가 시작되기도 전에 서버나 구독을 바꾸지 마세요.

2. inbounds: 로컬 포트 수신과 앱 연결

프로토콜보다 먼저 주소와 포트를 확인하세요

인바운드는 코어가 어느 위치에서 연결을 받는지 정의합니다. 한 기기에서 사용할 때 가장 먼저 확인할 필드는 listen과 port입니다. 앞의 필드는 바인딩할 로컬 주소를, 뒤의 필드는 앱이 연결할 포트를 지정합니다. 설정이 올바르더라도 포트를 반드시 열 수 있는 것은 아닙니다. 다른 클라이언트, 이전 코어 프로세스, 다른 서비스가 같은 주소와 포트를 사용 중이면 시작에 실패할 수 있으며, 로그에 보통 수신 오류가 표시됩니다.

127.0.0.1은 현재 기기에서만 접속할 수 있어 기본값으로 적합합니다. 수신 주소를 0.0.0.0으로 바꾸면 모든 IPv4 네트워크 인터페이스로 범위가 넓어지지만, 다른 기기에서 접속할 수 있는지는 방화벽과 네트워크 환경에 따라 달라집니다. 수신 범위를 넓히는 것은 로컬 연결 문제를 해결하는 일반적인 방법이 아닙니다. LAN 연결이 꼭 필요하다면 인증, 허용할 출처, 기기가 연결된 네트워크의 신뢰성도 함께 고려하세요.

SOCKS와 HTTP는 서로 다른 연결 프로토콜입니다

SOCKS 인바운드는 SOCKS 프록시를 직접 설정할 수 있는 앱에 적합하고, HTTP 인바운드는 HTTP 프록시 설정을 사용하는 프로그램을 위한 것입니다. 앱 설정의 프로토콜과 연결 포트가 일치해야 합니다. HTTP 프록시 주소를 SOCKS 포트에 입력하면 명확한 ‘프로토콜 불일치’ 메시지 없이 연결 직후 끊길 수 있습니다. 따라서 포트에 연결할 수 있다는 사실만으로 앱이 올바르게 연결되었다고 판단하지 마세요.

{
  "inbounds": [
    {
      "tag": "local-socks",
      "listen": "127.0.0.1",
      "port": 10808,
      "protocol": "socks",
      "settings": {
        "auth": "noauth",
        "udp": true
      }
    },
    {
      "tag": "local-http",
      "listen": "127.0.0.1",
      "port": 10809,
      "protocol": "http",
      "settings": {}
    }
  ]
}

예제에는 서로 독립적인 연결 지점 두 개가 있습니다. 포트는 이 문서에서 선택한 값일 뿐 모든 클라이언트가 고정으로 사용하는 포트가 아닙니다. 실제 사용 시에는 현재 클라이언트의 로컬 수신 설정을 확인한 뒤 앱의 프록시를 입력하세요. 브라우저 확장 프로그램, 시스템 프록시 설정, 터미널 환경 변수는 서로 다른 포트를 가리킬 수도 있습니다. 문제를 확인할 때는 테스트 중인 앱이 어느 설정을 읽는지 기록해 실제로 사용되지 않는 연결 지점만 수정하지 않도록 하세요.

SOCKS의 udp는 해당 UDP 연결 기능을 허용한다는 뜻이지, 모든 앱의 UDP 트래픽이 자동으로 코어에 들어간다는 뜻은 아닙니다. 앱이 SOCKS UDP를 지원하고 실제로 사용해야 하며, 이후 아웃바운드와 네트워크 경로도 UDP를 전달할 수 있어야 합니다. 이 필드만 켜서는 시스템 수준의 트래픽 가로채기가 필요한 상황을 대개 해결할 수 없습니다. Android의 VPN 연결과 데스크톱 앱에서 로컬 프록시를 직접 지정하는 방식은 서로 다른 연결 메커니즘입니다.

tag와 sniffing의 적용 범위

tag는 인바운드의 논리적 이름이며 라우팅의 inboundTag에서 참조할 수 있습니다. 수신 포트가 아니므로 태그를 바꾼다고 연결 방식이 달라지지는 않습니다. 역할이 드러나는 이름을 사용하고 중복되지 않도록 하세요. local-socks를 다른 문자열로 바꾸면 해당 이름으로 연결을 필터링하는 모든 규칙도 함께 수정해야 합니다. 그렇지 않으면 관련 규칙이 어느새 매칭되지 않게 됩니다.

{
  "sniffing": {
    "enabled": true,
    "destOverride": ["http", "tls"]
  }
}

위 객체는 개별 인바운드 안에 들어갑니다. 스니핑은 지원되는 초기 트래픽에서 HTTP 호스트 정보나 TLS 핸드셰이크의 서버 이름 같은 도메인 정보를 추출해 이후 라우팅을 돕습니다. HTTPS를 복호화하는 기능은 아니며, 모든 연결의 도메인을 알아낼 수 있는 것도 아닙니다. 트래픽에 식별할 정보가 없거나 앱이 다른 핸드셰이크 방식을 사용하거나 연결에서 필요한 정보가 아직 전달되지 않은 경우에는 예상한 결과를 얻지 못할 수 있습니다.

스니핑 옵션은 코어마다 다르며, 일부 옵션은 대상 주소를 바꿀지 여부에도 영향을 줍니다. 도메인 규칙을 적용하려고 모든 필드를 한꺼번에 켜지 마세요. 먼저 앱이 프록시에 도메인을 전달하는지 확인한 뒤 스니핑이 필요한지 판단하세요. 이미 도메인이 전달되었다면 규칙 순서나 출구 선택에 문제가 있을 가능성이 더 큽니다. 라우팅 전용 스니핑을 사용하려면 현재 코어가 해당 설정을 지원하는지 확인하세요.

인바운드 문제 해결 순서
증상우선 확인할 항목
코어가 시작하자마자 종료됨포트 사용 여부, 수신 주소 유효성, 필드 형식
코어는 정상인데 앱 접속 기록이 없음앱의 프록시 설정 위치, 프로토콜과 포트의 일치 여부
웹페이지는 열리지만 일부 앱에서 실패함앱이 시스템 프록시를 사용하는지, UDP를 사용하는지

인바운드를 확인할 때는 테스트 앱 하나와 연결 지점 하나만 사용하고, 해당 앱의 다른 프록시 플러그인은 잠시 끄세요. 로그에 연결이 나타나지 않으면 원격 아웃바운드를 바꾸지 마세요. 트래픽이 코어에 도달하지 않는다면 서버 설정을 아무리 정확하게 해도 결과가 달라지지 않습니다. 브라우저와 터미널을 나눠 확인하는 방법은 시스템 프록시가 작동하지 않을 때 확인할 항목을 참고하세요.

3. outbounds: 출구 태그와 원격 프로토콜

아웃바운드는 동작을 정의하며 규칙은 정의하지 않습니다

아웃바운드는 연결이 코어를 빠져나갈 때 사용할 방식을 정의합니다. 대표적인 유형으로는 대상에 직접 연결하는 freedom, 연결을 거부하는 blackhole, 원격 서버 프로토콜을 사용하는 아웃바운드가 있습니다. 라우팅은 아웃바운드를 선택하고, 아웃바운드는 데이터 전달을 수행합니다. 두 역할은 서로 대체할 수 없습니다. 이름이 proxy인 아웃바운드를 추가해도 모든 연결이 자동으로 그 출구를 선택하지 않습니다. 기본 출구와 라우팅 규칙에 따라 최종 경로가 결정됩니다.

일반적인 기존 설정에서는 라우팅 규칙에 걸리지 않은 연결이 아웃바운드 배열의 첫 번째 항목을 사용합니다. 따라서 배열 순서는 단순한 표시 순서가 아닙니다. 설정을 정리하면서 direct를 첫 번째로 옮기면 첫 항목의 프록시를 기본 경로로 사용하던 동작이 달라집니다. 편집 기록에 기본 출구를 명확히 남기고, 규칙을 추가할 때마다 어떤 규칙에도 해당하지 않는 테스트 대상으로 기본 경로를 확인하세요.

{
  "outbounds": [
    {
      "tag": "direct",
      "protocol": "freedom",
      "settings": {}
    },
    {
      "tag": "block",
      "protocol": "blackhole",
      "settings": {}
    }
  ]
}

이 코드 조각에는 직접 연결과 차단만 있으며 원격 서비스는 포함되지 않습니다. 출구 선택을 이해하기 위한 예제입니다. 차단 아웃바운드는 특정 연결을 명시적으로 거부할 때 사용할 수 있지만, 요청 실패를 모두 원격 네트워크 문제로 판단해서는 안 됩니다. 라우팅이 대상을 block으로 잘못 보냈다면 서버를 바꿔도 해결되지 않습니다. 문제를 확인할 때는 연결이 실제로 어떤 태그를 선택했는지 먼저 보고, 이어서 해당 출구의 연결 시도나 핸드셰이크 정보를 살펴보세요.

원격 연결 필드는 함께 확인해야 합니다

원격 아웃바운드의 프로토콜, 주소, 포트, 인증 정보, 전송 방식, 보안 계층은 서로 연관된 설정입니다. 공유 링크에 대부분의 정보가 포함될 수 있지만 가져오기 과정에서 형식 차이가 영향을 줄 수 있습니다. 가져온 뒤 연결되지 않는다면 클라이언트 편집 화면과 원본 정보를 항목별로 비교하세요. 특히 전송 경로, 서버 이름, 흐름 제어 옵션이 빠지지 않았는지 확인하고 주소와 포트만 확인하는 데 그치지 마세요.

아래는 기존 VLESS 아웃바운드 구조를 설명하는 예제입니다. 도메인 example.com과 모두 0인 식별자는 필드 위치를 설명하기 위한 값이며 실제로 연결 가능한 서비스가 아닙니다. 실제 원격 설정은 신뢰할 수 있는 서비스 설정을 가져와 사용하세요. 예제의 인증 정보나 서버 이름을 실제 연결에 사용하지 말고, 다른 프로토콜의 사용자 필드를 이 객체에 그대로 옮기지도 마세요.

{
  "tag": "proxy",
  "protocol": "vless",
  "settings": {
    "vnext": [
      {
        "address": "example.com",
        "port": 443,
        "users": [
          {
            "id": "00000000-0000-0000-0000-000000000000",
            "encryption": "none"
          }
        ]
      }
    ]
  },
  "streamSettings": {
    "network": "tcp",
    "security": "tls",
    "tlsSettings": {
      "serverName": "example.com"
    }
  }
}

encryption의 값은 프로토콜과 함께 해석해야 합니다. 예제의 none은 해당 VLESS 구조에서 사용하는 필드이며, 외부 TLS를 꺼도 같은 수준의 전송 보호를 받을 수 있다는 뜻이 아닙니다. VMess, Trojan, VLESS는 인증 방식과 필드 계층이 서로 다르므로 protocol만 바꿔서는 변환할 수 없습니다. 먼저 원격 서비스와 프로토콜을 일치시키고, 그다음 로컬 코어의 지원 여부를 확인하세요.

태그 참조와 클라이언트 업데이트 범위

아웃바운드 태그는 대소문자까지 포함해 라우팅의 outboundTag와 정확히 일치해야 합니다. 설정 목록의 표시 이름을 ‘프록시’로 바꿔도 코어의 아웃바운드 태그는 바뀌지 않을 수 있습니다. 클라이언트가 고정 태그를 생성할 수도 있으므로 직접 작성한 라우팅은 최종 실행 설정을 기준으로 하세요. 존재하지 않는 태그를 참조하면 설정 오류나 전달 실패가 발생할 수 있습니다. 코어가 출구를 알아서 추측하리라 기대하지 마세요.

구독을 갱신하면 노드 매개변수가 새로 적용되어 직접 수정한 원격 주소, 사용자 식별자, 전송 필드가 덮어써질 수 있습니다. 별도 항목의 설정을 계속 유지하려면 클라이언트에서 설정 복사와 구독 소속 항목을 어떻게 처리하는지 확인한 다음 복사본을 저장하세요. 공유 링크와 구독 주소는 갱신 방식이 다릅니다. 단일 노드 공유 링크와 구독 주소의 차이를 참고해 어느 계층을 수정해야 하는지 판단하세요.

기본 연결은 되지만 특정 대상만 실패한다면 현재 원격 매개변수는 그대로 두고 직접 연결과 프록시 라우팅을 비교하세요. 로그에서 연결 시도, 인증, 보안 핸드셰이크 실패가 확인될 때만 아웃바운드 필드를 우선 점검하면 됩니다. 모든 접속이 실패하는 경우와 특정 대상만 실패하는 경우를 구분해 기록하면 부분적인 라우팅 문제 때문에 노드 설정 전체를 반복해서 다시 만들지 않아도 됩니다.

4. streamSettings: 전송 및 보안 계층

프로토콜, 전송, 보안을 나눠서 살펴보세요

노드 설정은 세 계층으로 나눠 확인해야 합니다. 프록시 프로토콜은 양쪽이 인증하고 대상을 표현하는 방법을 정의하고, 전송 계층은 데이터를 전달할 연결 형태를 정하며, 보안 계층은 핸드셰이크와 암호화를 처리합니다. protocol, streamSettings.network, streamSettings.security는 각각 다른 계층에 해당합니다. 서로 호환되는 조합을 사용해야 하지만, 원격 포트가 443이라는 이유만으로 전송 방식이 WebSocket이거나 보안 방식이 TLS라고 단정할 수는 없습니다.

전송 설정은 원격 접속 지점과 일치해야 합니다. 기존에 TCP를 사용하는 서비스라면 클라이언트에서 network를 ws로 바꾸는 것만으로 WebSocket을 사용할 수 없습니다. 리버스 프록시가 해당 요청을 전달하는지, 경로가 일치하는지, 백엔드 서비스가 수신 중인지 등 서버 측 조건도 필요합니다. 클라이언트는 이미 제공되는 접속 방식을 사용할 뿐, 로컬 필드만으로 원격 서비스에 새로운 전송 경로를 만들 수 없습니다.

TLS의 도메인 이름과 서버 주소

address는 연결을 시도할 대상이고 serverName은 TLS 핸드셰이크에 사용하는 서버 이름입니다. 배포 방식에 따라 두 값이 다를 수 있습니다. 인증서 검증은 일반적으로 유효한 서버 이름과 일치해야 합니다. 주소에 연결되는데 TLS 핸드셰이크가 실패한다면 검증부터 끄지 말고 시스템 시간, 서버 이름, 인증서 상태를 확인하세요. 검증을 무시하면 신뢰 경계가 달라지고 원래 수정해야 할 설정 오류를 가릴 수 있습니다.

{
  "streamSettings": {
    "network": "ws",
    "security": "tls",
    "tlsSettings": {
      "serverName": "example.com"
    },
    "wsSettings": {
      "path": "/tunnel",
      "headers": {
        "Host": "example.com"
      }
    }
  }
}

이 코드는 전송 객체를 설명하기 위한 예제이며 경로와 도메인은 실제 서비스를 가리키지 않습니다. path는 서버 라우팅 설정과 정확히 일치해야 합니다. 맨 앞의 슬래시와 대소문자도 확인하세요. HTTP의 Host와 TLS 서버 이름은 서로 다른 단계에서 사용됩니다. 전자는 HTTP 요청 라우팅에, 후자는 보안 핸드셰이크에 관여합니다. 일부 환경에서는 두 값이 같아야 하지만 한 필드를 바꿨다고 다른 필드까지 자동으로 변경되는 것은 아닙니다.

경로에 쿼리 매개변수가 포함될 수도 있습니다. 설정을 텍스트로 복사할 때 원본 내용을 빠짐없이 보존하세요. 공유 링크의 일부가 URL 인코딩되어 있다면 클라이언트 편집 화면에는 디코딩된 형태로 표시될 수 있습니다. 문제를 확인할 때는 인코딩된 퍼센트 문자열과 디코딩된 문자를 혼동하지 말고 의미와 최종 실행 필드를 비교하세요. 이중 인코딩으로 원격 서비스가 다른 경로를 받지 않도록 주의하세요.

연결 단계와 관련 필드
단계주요 필드일반적인 확인 항목
대상 연결 시도address、port주소 확인, 네트워크 연결 가능 여부
보안 핸드셰이크security、serverName이름 일치 여부, 시간, 서버 설정
전송 연결network、path전송 유형, 리버스 프록시 경로
프로토콜 인증사용자 식별자, 프로토콜별 필드인증 정보, 프로토콜, 코어 지원 여부

REALITY와 흐름 제어를 혼동하지 마세요

REALITY는 Xray 생태계의 보안 핸드셰이크 방식이지, 임의로 바꿔 쓸 수 있는 프록시 프로토콜 이름이 아닙니다. 흔히 VLESS를 프로토콜로 사용하고 전송 보안 계층에 관련 필드를 설정합니다. 서버 이름, 공개 매개변수, 짧은 식별자는 서버에서 허용하는 값과 일치해야 합니다. 코어가 발전하면서 필드 이름도 바뀔 수 있으므로 설정을 옮길 때는 현재 코어 문서의 표기를 따르세요.

XTLS Vision의 흐름 제어 옵션은 REALITY와 다른 필드이며 모든 전송 방식에 자유롭게 조합할 수 있는 것도 아닙니다. flow를 입력할지는 서버의 프로토콜 조합과 실제로 사용하는 클라이언트 코어를 기준으로 결정해야 합니다. 인증이나 흐름 제어 오류가 발생하면 먼저 서비스에서 제공한 전체 매개변수를 복원한 뒤 해당 조합의 지원 여부를 확인하세요. 다른 전송 설정에 문서의 일부 필드만 덧붙이지 마세요.

두 기능의 차이가 궁금하다면 REALITY와 XTLS Vision의 작동 원리를 확인하세요. 실제 문제 해결은 로그에 표시된 실패 단계부터 시작해야 합니다. 연결 시도 실패는 주소를, 핸드셰이크 실패는 보안 매개변수를, 접속 경로 불일치는 전송 설정을 확인하세요. 전송 방식, 포트, 서버 이름을 한꺼번에 바꾸는 것보다 단계별로 확인하는 편이 정확한 원인을 찾기 쉽습니다.

5. routing: 매칭 순서와 트래픽 분할 범위

기본 출구를 정한 뒤 예외를 추가하세요

라우팅 규칙은 일반적으로 배열의 앞에서부터 차례로 확인하며, 조건에 맞는 첫 규칙이 연결 경로를 결정합니다. 문자열이 더 길거나 구체적이라는 이유만으로 규칙을 자동 선택하거나 여러 규칙의 결과를 합산하지 않습니다. 따라서 특정 도메인 예외는 일반적인 분류 규칙보다 앞에 두는 것이 보통입니다. 트래픽을 나누기 전에 기본 경로를 한 문장으로 정리하고 예외를 우선순위별로 나열하면 규칙 순서를 반복해서 바꾸는 일을 줄일 수 있습니다.

같은 규칙 안에서는 보통 서로 다른 조건 필드를 모두 충족해야 합니다. 반면 같은 도메인 또는 IP 목록의 여러 항목은 그중 하나라도 일치하면 됩니다. 예를 들어 inboundTag와 domain을 함께 쓰면 지정된 인바운드에서 들어오고 대상 도메인도 일치해야 합니다. 두 조건 중 하나만 맞으면 된다는 뜻이 아닙니다. 각 조건을 따로 시험하면 일치하지만 함께 사용하면 매칭되지 않는 문제가 여기서 생길 수 있습니다.

{
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        "type": "field",
        "domain": ["full:example.com"],
        "outboundTag": "block"
      },
      {
        "type": "field",
        "ip": ["geoip:private"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "domain": ["geosite:cn"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "ip": ["geoip:cn"],
        "outboundTag": "direct"
      }
    ]
  }
}

예제는 라우팅 일부만 보여줍니다. 전체 설정에 direct와 block이 정의되어 있고 도메인 및 IP 분류 데이터를 읽을 수 있어야 합니다. 규칙에 걸리지 않은 연결을 프록시로 보내려면 실제 원격 프록시를 기본 아웃바운드로 설정해야 합니다. 이 코드 자체는 프록시를 만들지 않습니다. 첫 번째 규칙은 문서용 예제 도메인을 차단 출구로 보내 일반 규칙보다 앞선 정확한 예외가 어떻게 적용되는지 보여줍니다.

도메인 형식과 분류 데이터

full:은 도메인을 정확히 일치시키고, domain:은 해당 도메인과 하위 도메인 범위를 대상으로 하며, regexp:는 정규 표현식을 사용합니다. 접두사가 없는 문자열은 키워드 매칭으로 처리될 수 있으므로 완전한 도메인으로 간주하지 마세요. 특정 사이트만 매칭하려면 의도가 명확한 접두사를 우선 사용하고 실제 연결 기록으로 확인하세요. 정규 표현식은 간단하게 작성해 유지 관리가 어려운 광범위한 패턴을 피하세요.

geosite와 geoip는 서로 다른 종류의 데이터를 참조합니다. 전자는 도메인 분류를, 후자는 IP 대역을 제공합니다. 코어가 대상의 지역을 실시간으로 판단하는 기능은 아닙니다. 데이터 파일 누락, 분류 이름 오류, 잘못된 경로, 오래된 리소스는 로드나 매칭에 영향을 줄 수 있습니다. 노드 구독을 갱신해도 이 두 리소스까지 함께 업데이트된다고 볼 수는 없습니다. 각 리소스 관리 위치에서 별도로 확인하세요.

domainStrategy는 추가 DNS 조회 시점을 정합니다

AsIs는 일반적으로 연결에 전달된 대상 형식으로 매칭하며 라우팅을 위해 도메인을 IP로 추가 변환하지 않습니다. IPIfNonMatch는 도메인에 맞는 규칙이 없을 때 조회를 시도한 뒤 IP 규칙을 확인합니다. IPOnDemand는 매칭 과정에서 IP 조건이 필요할 때 조회합니다. 세부 동작은 코어마다 다를 수 있으므로 관련 문서를 확인하세요. 다만 세 옵션 모두 라우팅 단계에 영향을 줄 뿐, 시스템의 모든 도메인 조회를 제어하지는 않습니다.

조회가 활성화되면 일부 연결에 추가 조회가 발생하고 그 결과가 라우팅에 영향을 줄 수 있습니다. 범위가 넓은 IP 규칙을 앞에 두면 도메인 분류를 기준으로 하려던 동작이 바뀔 수 있습니다. 먼저 코어가 도메인과 IP 중 무엇을 받았는지 확인하고, 다음으로 규칙 순서를 살펴본 뒤 조회 전략을 점검하세요. 입력 경로를 확인하지 않고 domainStrategy만 바꾸면 같은 사이트가 앱마다 다른 출구를 사용하는 이유를 설명하기 어렵습니다.

규칙 작성 시 자주 하는 오해
오해실제 적용 범위
도메인 규칙이 모든 IP 요청을 알아서 판별함인바운드에서 도메인을 전달하거나 지원되는 스니핑으로 정보를 얻어야 함
라우팅 모드가 앱의 프록시 설정도 바꿈라우팅 규칙은 코어에 들어온 연결만 처리함
분류 데이터가 언제나 빠짐없이 완전함분류 데이터는 관리되는 목록이므로 누락되거나 변경될 수 있음

기존 연결은 규칙을 바꾼 뒤에도 이전 경로를 사용할 수 있고 브라우저가 연결을 재사용할 수도 있습니다. 규칙을 수정한 뒤에는 새 테스트 연결을 만들고, 필요하면 테스트 앱을 재시작하세요. 페이지 새로고침만으로는 부족할 수 있습니다. 로그 결과가 예상과 다르면 정확한 도메인 규칙 하나로 인바운드와 출구를 먼저 확인한 다음 분류 규칙을 단계별로 다시 적용하세요. 자세한 트래픽 분할 방법은 geosite·geoip 라우팅 규칙 실전을 참고하세요.

6. dns: 조회 출처와 연결 경로

먼저 도메인 조회를 요청하는 주체를 확인하세요

코어 DNS를 설정해도 운영체제, 브라우저, 모든 앱이 해당 설정을 사용하는 것은 아닙니다. 앱이 먼저 로컬에서 도메인을 조회한 뒤 IP를 프록시에 전달할 수도 있고, 도메인을 SOCKS 또는 HTTP 연결 지점에 직접 전달해 이후 단계에서 처리할 수도 있습니다. 브라우저의 보안 DNS, 시스템 조회 캐시, 앱 내부 조회 라이브러리가 서로 다른 경로를 만들 수 있습니다. 문제를 해결하려면 대상이 도메인으로 코어에 들어오는지 IP로 들어오는지 먼저 확인하세요.

최상위 dns는 라우팅에서 IP 판별을 추가로 수행하거나 일부 아웃바운드 정책의 대상을 확인하는 등 코어에서 필요한 도메인 조회를 제공합니다. 이 설정은 로컬 DNS 포트를 자동으로 수신한다는 뜻이 아닙니다. 앱이 코어를 우회해 독립적으로 조회한다면 이 객체만 추가해도 앱 동작은 바뀌지 않습니다. 클라이언트가 지원하는 트래픽 가로채기 방식이나 앱 설정을 사용하세요. 두 역할을 혼동하지 마세요.

기본 서버와 조회 전략

아래 예제는 일반 DNS 서버 주소를 사용하고 조회 대상을 IPv4로 제한합니다. 명시적인 예시일 뿐 모든 네트워크에 적합하다는 뜻은 아닙니다. 테스트할 때는 현재 네트워크에서 연결할 수 있고 사용 목적에 맞는 조회 서비스를 선택하세요. 서버 자체에 연결할 수 없다면 도메인 라우팅을 바꿔도 조회가 자동으로 성공하지 않습니다. 근거 없이 서버를 계속 추가하지 마세요.

{
  "dns": {
    "hosts": {
      "manual.example": "192.0.2.10"
    },
    "servers": [
      "1.1.1.1",
      "8.8.8.8"
    ],
    "queryStrategy": "UseIPv4"
  }
}

hosts 예제에는 문서용으로 예약된 주소를 사용합니다. 실제 서비스에 접속하기 위한 값이 아닙니다. 고정 매핑은 통제된 테스트에 유용하지만 정상적인 주소 갱신을 우회합니다. 대상이 이전한 뒤 예전 매핑을 지우지 않으면 오랫동안 잘못된 주소를 가리킬 수 있습니다. 조회 문제를 확인할 때는 코어의 매핑과 시스템 hosts 파일을 따로 살펴보세요. 두 파일은 서로 다른 설정 출처입니다.

UseIPv4, UseIPv6, UseIP는 코어 조회에서 사용할 주소 계열의 우선순위나 범위를 제어합니다. IPv4로 제한하면 IPv6 경로 문제를 확인하는 데 도움이 될 수 있지만 모든 환경에 영구적으로 적합한 설정은 아닙니다. 대상이 IPv6 주소만 제공한다면 IPv4만 사용해서는 연결 가능한 주소를 얻을 수 없습니다. 네트워크가 듀얼 스택을 완전히 지원한다면 실제 연결 상태와 사용 목적에 따라 결정하고 예제를 무조건 복사하지 마세요.

도메인별 조회와 대체 동작을 함께 설계하세요

기존 설정에서는 서버 객체를 사용해 특정 도메인의 조회 서비스를 선택할 수 있습니다. 도메인별 우선 서버, 매칭되지 않을 때의 기본 조회, 조회 실패 후 대체 동작에는 여러 필드가 함께 작용할 수 있습니다. domains 목록 하나만 작성한다고 다른 서버가 완전히 배제되는 것은 아닙니다. 조회 경로를 명확히 제한해야 한다면 현재 코어의 대체 옵션을 확인하고, 조회 성공뿐 아니라 실패 상황도 검증하세요.

{
  "dns": {
    "servers": [
      {
        "address": "1.1.1.1",
        "domains": ["domain:example.com"]
      },
      "8.8.8.8"
    ],
    "queryStrategy": "UseIP"
  }
}

예제는 도메인에 따라 조회 서비스를 선택하는 관계를 보여줄 뿐, 조회가 반드시 프록시를 통과한다고 보장하지 않습니다. DNS 요청이 라우팅 대상인지, 직접 연결되는지, 특정 출구로 전달되는지는 서버 주소 형식과 코어 구현, 관련 설정에 따라 달라집니다. 일반 UDP 조회, HTTPS 조회, 로컬 처리 의미가 포함된 주소 표기는 서로 다른 경로를 사용할 수 있습니다. 설정할 때는 ‘어느 DNS 서버를 선택하는가’와 ‘그 서버에 어떻게 접속하는가’를 나눠 확인하세요.

도메인 형식의 암호화 DNS 서비스를 사용할 때는 부트스트랩 조회도 고려해야 합니다. 서비스에 연결하려면 먼저 해당 서비스의 주소를 알아야 합니다. 이 단계가 아직 만들어지지 않은 동일한 조회 경로에 의존하면 순환이 발생할 수 있습니다. 원격 서버 자체가 도메인 주소라면 순환을 더 알아보기 어렵습니다. 시작 단계에서 추적할 수 있는 조회 경로를 남겨 두고 로그에서 서비스 주소가 먼저 확인되는지 살펴보세요.

DNS와 라우팅 필드의 역할 구분
필드 위치제어 항목
dns.queryStrategy코어 DNS 조회에서 사용할 주소 계열 전략
routing.domainStrategy라우팅 매칭 시 도메인을 IP로 조회할지 여부
아웃바운드 프로토콜 내부의 조회 전략아웃바운드가 대상 도메인을 처리하는 방식. 필드는 프로토콜과 코어에 따라 다름

설정을 바꾼 뒤에는 캐시도 고려해야 합니다. 앱, 시스템, 코어가 각각 조회 결과를 보관할 수 있어 한 구성 요소만 재시작해도 동작이 바로 바뀌지 않을 수 있습니다. 테스트할 때 조회 대상, 주소 계열, 반환 결과를 기록하고 새 연결을 만드세요. 주소를 얻지 못한 경우와 주소는 얻었지만 연결에 실패한 경우를 구분해야 합니다. 후자는 라우팅과 출구를 더 확인해야 하며, 도메인과 관련 있다는 이유만으로 계속 DNS 문제로 돌리지 마세요.

7. policy와 log: 연결 정책 및 관찰 정보

정책 레벨은 연결 우선순위가 아닙니다

policy는 연결 처리 정책과 일부 통계 옵션을 설정합니다. 일반적인 기존 구조에서는 levels에 사용자 레벨별 매개변수를 구성합니다. 여기서 레벨은 속도 등급이 아니며 특정 연결에 더 많은 대역폭을 보장하지도 않습니다. 프로토콜과 사용자 설정이 해당 레벨을 실제로 참조해야 정책이 연결에 적용됩니다. 사용되지 않는 레벨을 완벽히 작성해도 실행 중 자동으로 활성화되지는 않습니다.

정책을 ‘범용 속도 향상 옵션’처럼 묶어서 적용하지 마세요. 핸드셰이크 제한 시간, 유휴 연결 시간, 한쪽 데이터가 끝난 뒤의 대기 시간, 버퍼 설정은 서로 다른 문제를 해결합니다. 대기 시간을 줄이면 끊어진 연결을 더 빨리 정리할 수 있지만 느린 핸드셰이크나 오랫동안 데이터가 없는 앱에 영향을 줄 수도 있습니다. 값을 늘려도 원격 인증 실패, DNS 조회 실패, 로컬 연결 프로토콜 오류는 해결되지 않습니다.

{
  "policy": {
    "levels": {
      "0": {
        "handshake": 4,
        "connIdle": 300,
        "uplinkOnly": 2,
        "downlinkOnly": 5
      }
    }
  }
}

예제는 레벨 0으로 구조를 보여줍니다. 키 이름은 문자열이며 시간 값은 각 필드 규칙에 따라 초 단위로 입력합니다. 구조 설명을 위한 매개변수 모음이지 모든 네트워크에 권장되는 조합은 아닙니다. 현재 코어의 기본 정책으로 충분하다면 이 항목을 생략해도 됩니다. 측정 근거가 없다면 출처를 알 수 없는 정책을 많이 복사하는 것보다 사용자 지정 필드를 줄이는 편이 관리하기 쉽습니다.

시간 제한 필드는 연결 상태와 함께 살펴보세요

handshake는 해당 연결의 핸드셰이크 대기 시간을 제한하고, connIdle은 연결이 유휴 상태에 머무는 시간과 관련됩니다. uplinkOnly와 downlinkOnly는 한쪽이 종료된 뒤 다른 방향의 처리를 기다리는 시간에 해당합니다. 적용되는 프로토콜과 상태 전환은 현재 코어 문서를 확인하세요. 이 값은 ‘웹페이지를 최대 몇 초 기다리는가’를 뜻하지 않으며 앱 자체의 요청 시간 제한을 대신하지도 않습니다.

예를 들어 장시간 연결은 업무 데이터가 오가지 않을 때 앱의 하트비트로 연결을 유지할 수 있습니다. 유휴 제한 시간이 너무 짧으면 연결이 주기적으로 다시 만들어지는 것처럼 보일 수 있습니다. 이때 클라이언트 값을 무작정 늘리기보다 앱 하트비트, 서버 제한 시간, 중간 네트워크 장비를 함께 확인하세요. 수정 전 원래 값을 기록하고, 수정 후에는 유휴 상태와 지속 전송 상태를 각각 테스트해야 어떤 상태에 영향이 있었는지 알 수 있습니다.

정책을 조정할 때 관찰할 항목
설정관찰할 현상이 결과만으로 추론할 수 없는 내용
핸드셰이크 대기연결 설정 단계가 지나치게 일찍 종료되는지원격 인증 정보가 반드시 올바르다는 점
유휴 대기업무 데이터가 없을 때 연결이 다시 만들어지는지지속 전송 속도가 반드시 달라진다는 점
통계 옵션해당 통계 기능에 필요한 조건이 갖춰졌는지클라이언트 화면에 모든 카운터가 표시된다는 점

로그 수준과 통계는 별개의 설정입니다

로그는 무슨 일이 있었는지 설명하고, 통계는 특정 연결이나 사용자의 카운트를 집계합니다. 서로 같은 기능이 아닙니다. 일부 통계 기능을 사용하려면 정책 옵션뿐 아니라 최상위 통계 객체, 지원되는 프로토콜 식별자, 조회 인터페이스도 필요합니다. statsUserUplink 필드 하나만 켠다고 GUI 클라이언트에 해당 화면이 나타나는 것은 아닙니다. 클라이언트가 별도의 수집 방식을 사용하거나 해당 통계를 전혀 읽지 않을 수도 있습니다.

{
  "log": {
    "loglevel": "warning"
  }
}

평소에는 적절한 로그 수준을 유지하고 문제를 확인할 때만 코어가 지원하는 범위에서 일시적으로 상세도를 높이세요. warning은 경고와 오류를 살펴보기에 적합하지만 모든 연결의 전체 경로를 기록하지는 않을 수 있습니다. 라우팅을 확인하려면 액세스 로그나 클라이언트의 연결 기록도 필요할 수 있습니다. 성공한 연결이 로그에 없다고 해서 트래픽이 코어를 통과하지 않았다고 단정하지 말고, 먼저 현재 기록 범위를 확인하세요.

로그 파일 경로를 지정할 때는 디렉터리 존재 여부, 실행 계정의 쓰기 권한, 클라이언트의 경로 허용 여부를 확인하세요. 데스크톱과 Android 앱의 쓰기 가능 디렉터리는 다르므로 다른 플랫폼의 절대 경로를 그대로 복사할 수 없습니다. 문제 해결을 위해 서비스에 더 높은 권한을 부여할 필요는 없습니다. 먼저 클라이언트의 기존 로그 화면을 사용하고 필요한 부분만 내보낸 뒤, 별도 파일을 설정할지 판단하세요.

로그에는 대상 도메인, 원격 주소, 인증 관련 정보가 포함될 수 있습니다. 공유하기 전에 민감한 항목을 하나씩 가리고 오류 유형, 발생 단계, 필요한 필드 경로는 남기세요. 테스트가 끝나면 평소 기록 범위로 되돌려 불필요한 파일이 계속 쌓이지 않도록 하세요. 증상을 더 확인하려면 경고 한 줄만 보고 전체 설정을 다시 만들기보다 FAQ의 문제 해결 항목에서 관련 내용을 찾아보세요.

8. 전체 검증, 마이그레이션, 문제 해결

단계별로 검증하고 한 번의 속도 테스트로 대신하지 마세요

전체 검증은 파일을 읽을 수 있는지, 코어가 시작되는지, 앱이 인바운드로 연결되는지, 어떤 출구가 선택되는지, 원격 핸드셰이크가 완료되는지, 대상에서 실제 데이터가 반환되는지를 확인해야 합니다. 클라이언트의 테스트 기능은 이 중 일부만 확인하는 경우가 많습니다. 테스트 한 번이 성공해도 브라우저 프록시가 설정된 것은 아닙니다. 실패하더라도 노드를 전혀 사용할 수 없다는 뜻은 아닙니다. 테스트 대상이나 방식이 실제 사용 상황과 다를 수 있습니다.

첫 단계에서는 설정 구조와 시작 여부만 확인합니다. 파일을 저장한 뒤 클라이언트의 설정 검사 기능이나 시작 로그로 파싱 결과를 확인하고 객체 계층, 필드 유형, 지원되지 않는 필드를 살펴보세요. 오류가 있으면 유효한 오류 중 가장 먼저 발생한 항목부터 처리하세요. 뒤에 나오는 오류는 앞선 실패의 연쇄 결과일 수 있습니다. 코어가 시작되기 전에는 DNS나 라우팅 분류를 바꿀 필요가 없습니다. 아직 해당 기능이 실행 단계에 도달하지 않았기 때문입니다.

두 번째로 인바운드를 확인합니다. 수신 포트와 앱 설정이 일치하는지 확인하고, 앱 하나에서 새 요청을 보내 해당 기록이 생기는지 살펴보세요. 연결이 전혀 없다면 시스템 프록시, 앱 내 프록시, 플러그인 간 설정 우선순위를 확인하세요. 데스크톱 터미널은 브라우저 설정을 자동으로 따르지 않는 경우가 많고, Android 앱도 앱별 프록시 범위의 영향을 받을 수 있습니다. 이 단계에서는 원격 인증 필드보다 연결 경로부터 해결하세요.

최소 구성으로 정상 상태 복구하기

세 번째로 아웃바운드를 확인합니다. 원격 항목 하나와 단순한 라우팅만 남기고 출구 태그가 올바른지 확인한 다음 연결 시도, 전송 핸드셰이크, 인증 단계를 살펴보세요. 한 번에 서로 관련된 매개변수 하나만 수정하고 연결을 새로 만드세요. 정상 작동하던 설정에 트래픽 분할 규칙을 추가한 뒤 실패했다면 새 규칙부터 되돌리는 것이 좋습니다. 코어를 바꾼 뒤에만 실패한다면 모든 네트워크 조건을 의심하기보다 확장 필드와 프로토콜 지원 여부를 먼저 확인하세요.

네 번째로 DNS와 규칙 적용 범위를 확인합니다. 정확한 도메인 규칙이 필요한 대상, IP 판단이 필요한 대상, 어떤 규칙에도 해당하지 않는 대상을 각각 하나씩 선택해 경로를 확인하세요. 테스트 대상은 실제 사용 목적에 맞게 정하고 예상 출구를 기록하세요. 페이지가 열리는지만 확인하지 마세요. 연결에 성공했더라도 설계한 출구와 다르면 규칙을 수정해야 합니다. 그렇지 않으면 이후 트래픽이 예상과 다른 경로로 전달될 수 있습니다.

실패 단계에 따라 수정 범위를 좁히세요
실패 단계주요 확인 항목변경하지 않을 항목
파일 읽기JSON 문법, 객체 계층, 필드 지원 여부서버 선택
로컬 연결수신 주소, 포트, 앱의 프록시 설정 위치원격 전송 경로
출구 연결 시도주소 확인, 포트, 네트워크 경로정책 시간 제한 설정
보안 또는 프로토콜 핸드셰이크서버 이름, 인증 정보, 전송 조합관련 없는 도메인 분류 데이터
일부 대상에서만 실패규칙 순서, 조회 결과, 대상 상태이미 검증한 기본 연결

클라이언트 간 이동 후에는 최종 설정 결과를 확인하세요

설정을 다른 기기에 복사하는 것만으로 마이그레이션이 완료되는 것은 아닙니다. 대상 클라이언트가 실제로 어떤 코어를 실행하는지 확인한 뒤 사용자 지정 설정 위치, 리소스 데이터 경로, 로그 경로, 로컬 수신 주소를 점검하세요. 데스크톱에서는 v2rayN을, Android에서는 필요에 따라 v2rayNG 또는 v2flyNG를 선택할 수 있습니다. 설치 안내는 클라이언트 페이지에서, 클라이언트별 역할 차이는 클라이언트 선택 가이드에서 확인하세요.

공유 링크나 구독을 기본 JSON으로 변환할 때 가져오기에 성공했다는 사실은 클라이언트가 입력을 받아들였다는 뜻일 뿐, 모든 확장 필드가 보존되었다는 뜻은 아닙니다. 항목 편집 화면에서 프로토콜, 전송, 보안 계층, 흐름 제어를 확인하고 최종 실행 설정도 살펴보세요. 변환 과정에서는 경로 인코딩, 기본값 누락, 필드 이름 변경에 특히 주의하세요. 형식별 차이는 구독 형식과 기본 JSON 비교를 참고하세요.

한 기기에서 여러 클라이언트를 동시에 실행하면 검증을 방해할 수 있습니다. 포트를 서로 차지하거나 시스템 프록시 설정을 반복해서 바꾸거나 코어 프로세스를 계속 실행 상태로 남길 수 있습니다. 테스트 대상을 전환하기 전에 기존 연결을 정상적으로 중지하고 새 클라이언트가 예상한 연결 지점을 사용하도록 하세요. 창을 닫는 것만으로 서비스가 종료되었다고 판단하지 말고, 현재 상태를 기록하기 전에 출처가 불분명한 프로세스를 한꺼번에 종료하지 마세요.

테스트 기록에는 설정 출처, 현재 코어, 수정한 필드, 발생 시각, 테스트 앱, 오류 단계를 남기고 전체 인증 정보는 기록하지 마세요. 문제가 어느 계층에 있는지 알 수 없다면 1장의 직접 연결 기본 설정으로 돌아간 뒤 원격 아웃바운드, 전송, 라우팅, DNS를 하나씩 복원하세요. 각 단계를 복원할 때마다 작동하는 복사본을 남겨 두면 문제가 생겼을 때 처음부터 설정을 조합하지 않고 마지막으로 확인된 상태로 돌아갈 수 있습니다.