V2Ray 설정 파일 구조 해부: inbounds, outbounds, routing의 역할

최소한의 V2Ray JSON 설정을 구간별로 살펴보고 inbounds·outbounds·routing의 주요 필드와 그래픽 클라이언트와 설정 파일의 관계를 설명합니다.

이 글의 핵심

노드를 가져올 수는 있지만 핵심 설정과 로그 필드를 이해하기 어려운 사용자에게 적합합니다. 로컬 리스너, 원격 연결, 규칙 기반 분기가 각각 어느 구역에 있는지 파악하고, tag를 따라 요청의 진입점과 적용된 규칙, 최종 출구를 확인할 수 있습니다.

전체 구조부터 보기: 설정 파일은 노드 하나가 아닙니다

V2Ray 5.x는 JSON으로 코어의 실행 방식을 정의합니다. 완전한 클라이언트 설정에는 서버 주소와 사용자 ID뿐 아니라 로컬 트래픽 진입점, 외부로 나가는 출구, DNS 동작, 로그 수준, 라우팅 규칙도 포함됩니다. 그래픽 클라이언트에서 보이는 ‘노드 하나’는 주로 프록시 아웃바운드의 서버 매개변수 묶음에 해당하며, 전체 실행 설정과 같지 않습니다.

설정을 읽을 때는 먼저 프로토콜 세부 사항을 내려놓고 데이터 흐름을 세 단계로 이해하면 됩니다. 애플리케이션이 로컬 리스닝 포트에 요청을 전달하면 inbounds가 요청을 받고 식별합니다. 이어 routing이 도메인, IP, 포트 또는 프로토콜에 따라 대상을 선택하고, 마지막으로 특정 outbounds 항목이 전송합니다. 인바운드와 아웃바운드는 tag로 이름을 지정하며, 라우팅 규칙은 이 이름을 참조합니다.

애플리케이션 요청인바운드 수신도메인 스니핑규칙 매칭아웃바운드 선택대상으로 전송

최상위 필드의 작성 순서에는 정해진 규칙이 없습니다. JSON 파서는 routingoutbounds보다 앞에 있다고 실행 결과를 바꾸지 않습니다. 하지만 배열 내부의 순서는 중요할 수 있습니다. 라우팅 규칙은 일반적으로 위에서 아래로 매칭되고, 일치하는 규칙이 없는 트래픽은 보통 첫 번째 아웃바운드로 전달됩니다. 따라서 읽을 때는 tag와 배열 위치를 함께 확인해야 합니다.

inbounds

방향
애플리케이션에서 코어로
주요 프로토콜
SOCKS、HTTP
핵심 필드
listen、port、tag
일반적인 포트
10808

로컬에서 어떤 주소와 포트가 트래픽을 코어에 전달할 수 있는지 결정합니다.

outbounds

방향
코어에서 대상으로
프록시 프로토콜
VMess、VLESS
보조 출구
freedom、blackhole
식별 방식
tag

노드 서버 정보와 전송 계층 및 TLS 매개변수는 주로 이곳에 모입니다.

routing

규칙 유형
field
매칭 대상
도메인, IP, 포트
실행 결과
outboundTag
검사 순서
위에서 아래로

출구를 선택할 뿐 원격 프로토콜 연결을 직접 수립하지는 않습니다.

dns 및 log

dns
해석 전략
log
로그 수준
일반 수준
warning
문제 해결용 수준
info

모든 설정에 명시적으로 작성되지는 않지만, 이름 해석과 문제 해결에 직접 영향을 줍니다.

inbounds: 트래픽은 로컬의 어디로 들어오는가

inbounds는 배열이며 각 객체가 하나의 로컬 진입점을 나타냅니다. 데스크톱 클라이언트에서는 SOCKS와 HTTP 인바운드를 함께 만들고, SOCKS를 127.0.0.1:10808, HTTP를 127.0.0.1:10809에 배치하는 방식이 흔합니다. 브라우저, 명령줄 도구 또는 시스템 프록시가 해당 포트로 요청을 보내야 코어가 연결 처리를 시작합니다.

listen은 리스닝 주소를 결정합니다. 127.0.0.1로 설정하면 로컬 연결만 허용하고, 0.0.0.0으로 설정하면 모든 네트워크 인터페이스에서 수신하므로 LAN 기기가 해당 포트에 접근할 수 있습니다. LAN 프록시가 꼭 필요한 경우가 아니라면 데스크톱 환경에서는 로컬 루프백 주소가 더 적합합니다. port는 다른 프로그램이 사용하지 않아야 하며, 중복 사용 시 로그에 bind 또는 address already in use가 자주 나타납니다.

10808
예시 SOCKS 포트
10809
예시 HTTP 포트
127.0.0.1
로컬에서만 수신
53
표준 DNS 포트

protocol은 진입점 프로토콜을 뜻하며 원격 노드 프로토콜이 아닙니다. SOCKS 인바운드가 VLESS 또는 VMess 아웃바운드로 요청을 전달하는 것도 가능합니다. 두 프로토콜이 같아야 한다는 관계는 없습니다. settings에는 해당 인바운드 프로토콜 전용 매개변수가 저장되며, SOCKS 인바운드의 udp: true는 UDP 요청 수신을 허용한다는 뜻입니다.

sniffing은 연결 내용에서 대상 도메인을 복원하는 기능입니다. 애플리케이션이 먼저 도메인을 IP로 변환한 뒤 로컬 프록시에 연결하는 경우가 있는데, 코어가 IP만 확인하면 도메인 기반 라우팅 규칙이 매칭되지 않습니다. 스니핑을 활성화하고 destOverride를 설정하면 코어가 해당 HTTP 및 TLS 트래픽에서 도메인을 식별한 뒤 routing에 전달할 수 있습니다.

{
  "tag": "socks-in",
  "listen": "127.0.0.1",
  "port": 10808,
  "protocol": "socks",
  "settings": {
    "auth": "noauth",
    "udp": true
  },
  "sniffing": {
    "enabled": true,
    "destOverride": ["http", "tls"]
  }
}

outbounds: 노드, 직접 연결, 차단 출구

outbounds 역시 배열입니다. 프록시 노드는 보통 그중 하나일 뿐이며, 실제 클라이언트는 직접 연결용 출구와 차단용 출구도 추가로 생성합니다. 프록시 출구는 VMess나 VLESS 같은 프로토콜로 원격 서버에 연결하고, freedom은 코어가 대상에 직접 접속하게 하며, blackhole은 규칙에 선택된 연결을 종료합니다.

프록시 아웃바운드는 다시 두 계층으로 나눌 수 있습니다. settings는 프로토콜 인증 정보와 서버 포트를 설명합니다. 예를 들어 VMess의 주소, 포트, 사용자 ID, 보안 매개변수가 여기에 들어갑니다. streamSettings는 TCP, WebSocket, TLS 또는 Reality 같은 하위 전송 및 보안 계층을 설명합니다. 프로토콜 필드가 올바르더라도 전송 경로, SNI 또는 보안 계층이 일치하지 않으면 연결은 실패합니다.

VMess + WebSocket + TLS

protocol
vmess
network
ws
security
tls
원격 포트
443
경로
/v2ray

사용자 인증 정보는 settings에, 전송 경로와 TLS는 streamSettings에 둡니다.

VLESS + TCP + Reality

protocol
vless
network
tcp
security
reality
flow
xtls-rprx-vision
핑거프린트
chrome

Reality 매개변수는 서버 설정과 대응해야 하며 security 필드 이름만 바꿔서는 안 됩니다.

직접 연결 출구

tag
direct
protocol
freedom
원격 노드
필요 없음
용도
로컬 및 직접 연결 규칙

direct에 매칭되면 로컬 네트워크가 대상에 직접 연결합니다.

차단 출구

tag
block
protocol
blackhole
원격 연결
수립하지 않음
용도
지정 트래픽 차단

라우팅 규칙이 block을 참조하면 요청은 대상까지 계속 전달되지 않습니다.

아웃바운드의 tag는 설정을 점검할 때 가장 중요한 인덱스입니다. 라우팅 규칙에 "outboundTag": "direct"가 적혀 있다면 outbounds로 돌아가 tag가 direct인 객체를 찾으면 됩니다. 규칙이 존재하지 않는 tag를 참조하면 코어는 보통 대상 출구를 임의로 추측하지 않고 시작 단계에서 설정 오류를 보고합니다.

구독이 제공하는 것은 보통 노드 연결 매개변수이며, 로컬 포트와 로그 수준 및 모든 분기 규칙을 완전히 결정하지는 않습니다. v2rayN, v2rayNG 또는 v2flyNG는 구독을 가져온 뒤 노드 필드와 클라이언트 자체 설정을 합쳐 코어에 전달할 실행 설정을 생성합니다. 따라서 같은 구독이라도 클라이언트에 따라 생성되는 전체 JSON이 다를 수 있습니다.

결론: 아웃바운드 오류는 계층별로 먼저 확인

인증 실패라면 먼저 settings의 주소, 포트와 사용자 정보를 확인합니다. TLS, Reality 또는 WebSocket 핸드셰이크 실패라면 streamSettings를 점검합니다. 두 계층의 매개변수를 한꺼번에 수정하면 필드는 모두 갖춰진 것처럼 보여도 연결이 계속 시간 초과되는 상황이 가장 쉽게 발생합니다.

routing: 요청을 순서대로 지정 출구에 전달

routing.rules는 규칙 배열입니다. 일반적으로 field 유형을 사용하며 domain, ip, port, network, protocol, inboundTag 등의 조건으로 매칭할 수 있습니다. 규칙 자체가 데이터를 전달하는 것은 아니며, outboundTag로 어떤 아웃바운드가 처리할지 지정합니다.

규칙 순서에 따라 결과가 달라집니다. 첫 번째 규칙이 특정 도메인을 프록시로 보내고 두 번째 규칙이 더 넓은 도메인 집합을 직접 연결하도록 되어 있다면, 해당 도메인은 첫 번째 규칙에 매칭된 뒤 추가 검사를 받지 않습니다. 더 구체적인 차단 또는 강제 프록시 규칙은 앞에 두고, 범위가 넓은 직접 연결 규칙과 최종 대체 규칙은 뒤에 배치해야 합니다.

매칭 필드 매칭 대상 일반적인 용도 실행 결과
domain 전체 도메인, 접미사 또는 도메인 집합 사이트 유형별 트래픽 분기 지정한 outboundTag로 전달
ip 단일 IP, CIDR 또는 IP 집합 LAN 및 대상 네트워크 대역 분기 직접 연결, 프록시 또는 차단
port 단일 포트 또는 포트 범위 특정 서비스 트래픽 제어 해당 출구 선택
network tcp, udp 또는 둘 다 최종 대체 규칙 남은 연결에 적용
inboundTag 하나 이상의 인바운드 tag 로컬 진입점별로 다른 출구 사용 진입점 단위의 트래픽 분기 구현

domainStrategy는 라우터가 도메인의 IP를 언제 조회할지 결정합니다. AsIs는 원래 도메인으로 우선 매칭하며 IP 규칙을 위해 능동적으로 조회하지 않습니다. IPIfNonMatch는 먼저 도메인 규칙을 확인하고, 매칭되지 않으면 IP를 조회해 IP 규칙을 시도합니다. IPOnDemand는 IP가 필요할 가능성이 있는 규칙을 만나면 더 일찍 조회를 시작합니다. 어떤 방식을 선택할지는 규칙 설계에 따라야 하며, 더 공격적인 값이 항상 좋은 것은 아닙니다.

어떤 규칙에도 매칭되지 않으면 V2Ray는 보통 첫 번째 아웃바운드를 사용합니다. 암묵적인 순서에 의존하지 않으려면 마지막에 TCP와 UDP를 모두 포괄하는 대체 규칙을 추가하고 대상 outboundTag를 명시할 수 있습니다. 이렇게 하면 outbounds 순서를 바꿔도 기본 트래픽 방향이 의도치 않게 달라지지 않습니다.

{
  "domainStrategy": "IPIfNonMatch",
  "rules": [
    {
      "type": "field",
      "protocol": ["bittorrent"],
      "outboundTag": "block"
    },
    {
      "type": "field",
      "ip": ["geoip:private"],
      "outboundTag": "direct"
    },
    {
      "type": "field",
      "domain": ["geosite:cn"],
      "outboundTag": "direct"
    },
    {
      "type": "field",
      "network": "tcp,udp",
      "outboundTag": "proxy"
    }
  ]
}

결론: 라우팅 문제는 첫 번째 규칙부터 점검

먼저 대상 도메인이나 IP가 실제로 어떤 규칙에 매칭됐는지 확인한 다음, 해당 규칙이 참조하는 outboundTag를 점검합니다. 노드 연결은 정상인데 트래픽 방향이 잘못되었다면 대개 규칙 순서, 도메인 스니핑 또는 DNS 결과의 문제이지 프록시 프로토콜 자체의 고장은 아닙니다.

읽기 쉬운 최소 클라이언트 설정

아래 예시는 로그, SOCKS 인바운드 하나, 아웃바운드 세 개, 라우팅 규칙 네 개를 하나의 파일에 넣은 구성입니다. 예시 주소와 사용자 ID는 구조를 보여주기 위한 것이며 실제 노드로 사용할 수 없습니다. 설정 파일은 표준 JSON을 사용하므로 키와 문자열은 큰따옴표로 작성해야 하고, 끝에 불필요한 쉼표를 남길 수 없으며 주석을 직접 삽입할 수도 없습니다.

데이터 흐름을 보면 애플리케이션은 먼저 127.0.0.1:10808에 연결합니다. 코어는 라우팅을 확인해 BitTorrent 트래픽을 block으로 보내고, 사설 주소와 지정 도메인 집합은 direct로 보내며, 나머지 TCP 및 UDP 요청은 proxy로 보냅니다. proxy는 VMess, WebSocket, TLS를 사용해 예시 서버의 443 포트에 연결합니다.

{
  "log": {
    "loglevel": "warning"
  },
  "inbounds": [
    {
      "tag": "socks-in",
      "listen": "127.0.0.1",
      "port": 10808,
      "protocol": "socks",
      "settings": {
        "auth": "noauth",
        "udp": true
      },
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"]
      }
    }
  ],
  "outbounds": [
    {
      "tag": "proxy",
      "protocol": "vmess",
      "settings": {
        "vnext": [
          {
            "address": "server.example.com",
            "port": 443,
            "users": [
              {
                "id": "00000000-0000-4000-8000-000000000000",
                "alterId": 0,
                "security": "auto"
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "ws",
        "security": "tls",
        "tlsSettings": {
          "serverName": "server.example.com"
        },
        "wsSettings": {
          "path": "/v2ray"
        }
      }
    },
    {
      "tag": "direct",
      "protocol": "freedom",
      "settings": {}
    },
    {
      "tag": "block",
      "protocol": "blackhole",
      "settings": {}
    }
  ],
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        "type": "field",
        "protocol": ["bittorrent"],
        "outboundTag": "block"
      },
      {
        "type": "field",
        "ip": ["geoip:private"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "domain": ["geosite:cn"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "network": "tcp,udp",
        "outboundTag": "proxy"
      }
    ]
  }
}
  1. 먼저 JSON 문법을 확인합니다. 특히 큰따옴표, 쉼표, 대괄호가 서로 맞는지 살펴봅니다.
  2. 그다음 모든 tag가 고유한지, routing이 참조하는 outboundTag가 실제로 존재하는지 확인합니다.
  3. 인바운드 포트가 사용 중이 아닌지 확인하고 시스템 프록시 또는 애플리케이션이 같은 포트를 가리키게 합니다.
  4. 프록시 아웃바운드의 프로토콜, 서버 포트, 전송 방식과 보안 계층이 서로 맞는 조합인지 확인합니다.
  5. 마지막으로 규칙 순서를 확인하고, 대체 규칙이 더 구체적인 규칙 뒤에 있는지 점검합니다.

그래픽 클라이언트와 코어 JSON의 대응 관계

v2rayN, v2rayNG, v2flyNG는 자주 사용하는 필드를 양식으로 나누어 제공합니다. 노드 편집 화면의 주소, 포트, 사용자 ID, 전송 방식과 보안 설정은 주로 프록시 outbound에 매핑되고, 로컬 SOCKS 포트와 LAN 연결 허용 같은 옵션은 inbounds에 매핑됩니다. 라우팅 모드, 규칙 세트와 사용자 지정 규칙은 routing에 매핑됩니다.

v2rayN을 예로 들면 로컬 포트와 기본 동작은 보통 ‘설정’ → ‘매개변수 설정’에서 변경하고, 노드 매개변수는 서버 편집 화면에서 수정합니다. 클라이언트가 코어를 시작할 때 노드, 전역 매개변수와 라우팅 설정을 실행 설정으로 조합합니다. 임시로 생성된 코어 JSON을 직접 수정하면 다음 노드 전환, 구독 업데이트 또는 코어 재시작 때 다시 생성될 수 있습니다.

Android의 v2rayNG와 v2flyNG도 비슷한 계층 구조를 사용하지만 코어는 서로 다릅니다. v2rayNG는 Xray 코어를 사용하고 v2flyNG는 v2fly 코어를 사용합니다. 일부 고급 필드와 기본값이 다를 수 있으므로 한 클라이언트에서 내보낸 전체 설정을 다른 클라이언트의 화면 설정 파일로 그대로 사용할 수는 없습니다.

화면에는 노드가 하나뿐인데 실행 설정에는 왜 아웃바운드가 세 개일까요?

노드는 proxy 아웃바운드에만 대응합니다. 클라이언트는 직접 연결과 차단 규칙이 참조할 direct와 block도 추가로 생성하므로 outbounds 수는 보통 노드 수보다 많습니다.

생성된 JSON을 수정했는데 재시작하면 왜 원래대로 돌아오나요?

생성 파일은 실행 중 만들어지는 산출물입니다. ‘설정’ → ‘매개변수 설정’, 노드 편집 또는 라우팅 설정으로 돌아가 원본 데이터를 수정한 뒤 코어를 재시작해 확인해야 합니다.

구독을 가져온 뒤에도 로컬 포트가 왜 바뀌지 않나요?

구독은 주로 노드 연결 매개변수를 제공하며 로컬 리스닝 포트는 클라이언트 설정에 속합니다. SOCKS 포트가 여전히 10808인지 확인하고 시스템 프록시도 함께 업데이트하세요.

라우팅 규칙에 도메인을 적었는데 왜 여전히 잘못된 출구로 연결되나요?

먼저 해당되는 도메인 스니핑을 활성화한 다음 domainStrategy, DNS 결과와 규칙 순서를 확인합니다. 로그 수준을 일시적으로 info로 설정하면 실제 대상과 아웃바운드 tag를 확인할 수 있습니다.

로그 순서에 따라 시작 실패와 트래픽 분기 오류 확인

설정 문제는 시작 단계와 실행 단계로 나눌 수 있습니다. 시작 단계의 실패는 대개 JSON 문법, 필드 유형, 포트 중복 또는 잘못된 tag와 관련되며, 코어가 인바운드 리스닝을 만들지 못할 수도 있습니다. 실행 단계에서 발생하는 문제는 노드 매개변수, DNS, 규칙 매칭과 대상 네트워크에 더 관련되어 있습니다.

문제를 해결할 때 여러 구역을 한 번에 수정하지 마세요. 먼저 loglevelwarning에서 일시적으로 info로 바꾸고 문제를 한 번 재현해 시간을 기록합니다. 인바운드가 수신 대기하지 않는지, 규칙이 잘못된 출구를 선택했는지, proxy 아웃바운드 연결이 실패했는지 확인한 뒤 해당 구역만 수정합니다. 해결 후에는 warning으로 되돌려 일반 로그 양을 줄일 수 있습니다.

{
  "log": {
    "loglevel": "info"
  }
}

로그의 inbound tag, 대상 주소와 outbound tag를 연결하면 전체 경로를 추적할 수 있습니다. 예를 들어 요청이 socks-in으로 들어오고 대상이 특정 도메인으로 표시된 뒤 direct가 선택됐다면, 이번 연결에는 노드가 참여하지 않은 것입니다. 프록시를 예상했다면 VMess나 VLESS 인증 정보를 반복해서 수정하기보다 routing을 확인해야 합니다.

반대로 로그에 규칙이 proxy를 선택한 것으로 표시된 뒤 TLS 핸드셰이크나 연결 시간 초과가 발생한다면 라우팅은 기본 역할을 완료한 것입니다. 이때는 프록시 outbound의 주소, 포트, SNI, 전송 경로, 시스템 시간과 원격 접속 가능성을 확인해야 합니다.

결론: tag로 전체 경로 추적

문제 해결 절차를 ‘인바운드 tag → 대상 도메인 또는 IP → 매칭된 규칙 → 아웃바운드 tag → 전송 연결’로 고정하세요. 각 단계가 하나의 설정 구역에 대응하므로 포트 충돌, 트래픽 분기 오류와 노드 핸드셰이크 실패를 하나의 문제로 혼동하지 않을 수 있습니다.

v2rayN 다운로드 Windows, macOS, Android, Linux 클라이언트 확인