V2Ray 설정 파일 JSON 구조 해설: inbounds·outbounds와 routing 이해하기

log, dns, inbounds, outbounds, routing 순으로 클라이언트 설정 파일을 살펴보고 각 항목의 역할과 참조 관계, tag와 라우팅 규칙의 매칭 방식을 설명합니다.

이 글의 핵심 내용

구독을 가져온 뒤 핵심 설정을 이해하고 싶은 사용자를 위한 글입니다. 로컬 SOCKS 인바운드와 직결·차단 아웃바운드만 포함한 JSON으로 구조를 익힌 다음, 로그와 DNS, 포트, 태그, 규칙 순서를 차례로 확인합니다. 읽고 나면 ‘코어 시작 실패’, ‘로컬 포트 연결 안 됨’, ‘규칙 미적용’ 문제별로 어디를 확인해야 하는지 알 수 있습니다.

클라이언트 설정과 코어 설정 구분하기

v2rayN은 구독, 서버 항목, 라우팅 프리셋, 코어 프로세스를 관리하는 데스크톱 클라이언트입니다. 실제로 코어 설정을 읽고 연결을 처리하는 프로그램은 V2Ray 또는 Xray입니다. 클라이언트에 저장된 화면 설정이나 구독 그룹 데이터는 코어에 전달되는 런타임 JSON과 다릅니다. 문제를 해결할 때는 확장자가 .json이라는 이유만으로 파일을 수정하지 말고, 이번 실행에서 실제로 불러온 파일을 찾아야 합니다.

Android용 v2rayNG는 Xray 코어를 사용하고, v2flyNG는 V2Fly 코어를 사용합니다. 이 글에서 설명하는 것은 일반적인 V2Ray 설정 구조이며, 모든 항목을 두 코어에서 공통으로 사용할 수 있다는 뜻은 아닙니다. 예를 들어 REALITY는 Xray 관련 기능이므로 해당 설정을 V2Fly에 그대로 적용할 수 없습니다. 이름이 같은 streamSettings라도 전송 방식과 사용 가능한 옵션은 현재 코어에 맞춰 확인해야 합니다.

앱 연결 로컬 인바운드 규칙 매칭 아웃바운드 선택 대상 서버

이 흐름을 중심으로 설정을 읽으면 됩니다. DNS는 도메인 이름을 조회해야 할 때 관여하고, 로그는 전체 과정을 기록합니다. 시스템 프록시는 해당 설정을 따르는 앱에만 영향을 줍니다. 설정에 SOCKS 인바운드가 있다고 해서 모든 앱의 트래픽이 코어로 들어오는 것은 아닙니다. 인바운드 연결이 되지 않는다면 라우팅을 수정해도 보통 결과는 달라지지 않습니다.

전체 JSON 예시: 인바운드 1개, 아웃바운드 2개, 규칙 2개

아래는 구조를 익히기 위한 설정 예시이며, 원격 프록시 서버는 포함하지 않습니다. 127.0.0.1:10808에서 SOCKS 서비스를 제공하고, example.org와 정확히 일치하는 도메인 요청 및 TCP 25 포트 요청을 차단하며 나머지 연결은 직결합니다. 기존 구독 설정을 대체하는 용도로 사용하면 원래의 프록시 아웃바운드가 사라지므로 주의하세요.

{
  "log": {
    "loglevel": "warning"
  },
  "dns": {
    "servers": [
      "1.1.1.1"
    ]
  },
  "inbounds": [
    {
      "tag": "socks-local",
      "listen": "127.0.0.1",
      "port": 10808,
      "protocol": "socks",
      "settings": {
        "auth": "noauth",
        "udp": true
      }
    }
  ],
  "outbounds": [
    {
      "tag": "direct",
      "protocol": "freedom",
      "settings": {
        "domainStrategy": "UseIP"
      }
    },
    {
      "tag": "block",
      "protocol": "blackhole",
      "settings": {}
    }
  ],
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      {
        "type": "field",
        "inboundTag": [
          "socks-local"
        ],
        "domain": [
          "full:example.org"
        ],
        "outboundTag": "block"
      },
      {
        "type": "field",
        "network": "tcp",
        "port": "25",
        "outboundTag": "block"
      }
    ]
  }
}

최상위 객체의 키 순서는 보통 처리 순서를 결정하지 않습니다. 이 글에서는 읽기 편하도록 배치했습니다. 반면 배열의 순서는 동작에 직접 영향을 줄 수 있으며, 특히 outbounds의 첫 항목과 routing.rules의 순서가 중요합니다. ‘형식을 정리한다’는 이유로 이 두 배열을 임의로 정렬하지 마세요.

log: 로그로 시작 오류와 연결 오류 구분하기

log는 로그 출력을 제어하며 노드를 선택하지 않습니다. 예시에서는 loglevel만 설정하고 로그 파일은 지정하지 않았습니다. 실제 로그 출력 위치는 코어를 실행하는 방식에 따라 달라지며, 클라이언트가 로그 패널에 출력을 모을 수도 있습니다. 문제를 해결할 때는 시작 직후 처음 나타난 오류를 확인한 다음, 접속을 시도할 때 새로 추가된 기록을 살펴보세요.

항목 또는 증상의미다음 단계
loglevel: warning경고와 오류만 기록해 평소 로그 출력을 줄입니다.연결 정보를 더 확인해야 한다면 일시적으로 info로 변경합니다.
loglevel: debug디버깅 정보가 더 많이 출력되어 로그가 빠르게 늘어납니다.문제를 한 번 재현한 뒤 원래 수준으로 되돌리고, 공유 전에 민감한 정보를 삭제합니다.
address already in use다른 프로세스가 수신 주소와 포트를 사용 중입니다.10808 포트를 다른 코어 인스턴스가 이미 사용 중인지 확인합니다.
필드 파싱 또는 설정 파일 로드 오류코어가 아직 정상적으로 연결을 처리하는 단계까지 시작되지 않았습니다.필드 이름과 데이터 형식을 확인하고, 현재 코어가 해당 옵션을 지원하는지 살펴봅니다.

코어가 시작하자마자 종료된다면 DNS를 바꾸기 전에 설정 오류나 포트 충돌부터 해결하세요. 코어가 정상 실행 중이고 특정 요청만 실패한다면 해당 대상과 라우팅 결과, 연결 오류를 확인합니다. 로그에는 접속 도메인, 서버 주소, 로컬 경로가 포함될 수 있으므로 실행 설정 파일과 로그 전체를 공개 페이지에 올리지 마세요.

dns: DNS 서버 설정과 라우팅 정책은 별개입니다

dns.servers는 코어 내장 DNS가 사용할 DNS 서버를 지정합니다. 예시의 1.1.1.1은 일반 DNS 서버 주소로, 보통 UDP 53 포트를 통해 조회합니다. 여기에는 암호화 DNS가 설정되어 있지 않으며 DNS 조회가 자동으로 프록시를 거친다는 뜻도 아닙니다. DNS 서버에 실제로 연결할 수 있는지는 현재 네트워크에서 확인해야 합니다.

혼동하기 쉬운 것은 이름이 같은 두 필드입니다. routing.domainStrategy는 라우팅 매칭 단계에서 도메인을 어떻게 처리할지 정하고, freedom 아웃바운드의 settings.domainStrategy는 직결 연결을 만들 때 대상 도메인을 어떻게 조회할지 정합니다. 서로 다른 객체에 속하며 값과 역할도 다릅니다.

라우팅 매칭 단계

위치
routing.domainStrategy
예시 값
AsIs
역할
라우팅 매칭을 위해 대상 도메인을 별도로 조회하지 않습니다.
그 밖의 일반적인 값
IPIfNonMatch、IPOnDemand

AsIs는 연결 과정 전체에서 DNS 조회가 일어나지 않는다는 뜻이 아닙니다.

직결 아웃바운드 단계

위치
freedom.settings.domainStrategy
예시 값
UseIP
역할
내장 DNS로 대상 도메인을 IP 주소로 변환한 뒤 연결합니다.
조회에 사용하는 DNS
이 예시의 dns.servers

라우팅의 IPIfNonMatch를 이 위치에 넣지 마세요.

IPIfNonMatch는 도메인이 규칙과 일치하지 않을 때 조회를 시도한 뒤 IP 규칙을 확인합니다. IPOnDemand는 매칭 과정에서 IP가 필요할 때 조회를 시작합니다. 둘 다 ‘모든 트래픽을 프록시로 강제 전송’하는 설정은 아닙니다. 설정에 IP 규칙이 없다면 도메인 규칙 하나를 설명하기 위해 불필요한 조회 동작을 추가하지 말고 단순한 정책을 유지하세요.

inbounds: 앱이 연결된 인바운드 확인하기

inbounds는 연결을 받는 인바운드를 각각 정의하는 배열입니다. 이 예시에서 listen은 로컬 루프백 주소로 제한하고, port는 숫자 10808, protocol은 socks로 지정합니다. 이 세 필드가 앱이 어디에 어떤 프로토콜로 연결해야 하는지 결정합니다.

  1. 수신 주소 확인: 127.0.0.1은 로컬 기기에서만 접속할 수 있습니다. 0.0.0.0으로 변경하면 수신 범위가 넓어지므로, 문제 해결을 위해 인증 없는 인바운드를 외부에 노출하지 마세요.
  2. 프로토콜 확인: SOCKS 포트는 HTTP 프록시 포트와 다릅니다. HTTP 프록시만 지원하는 프로그램을 이 예시의 10808 포트에 연결해도 노드가 작동하는지 판단할 수 없습니다.
  3. 실제 포트 확인: 10808은 이 글의 예시 값이며 모든 클라이언트에서 고정으로 사용하는 포트가 아닙니다. v2rayN의 ‘설정’ → ‘매개변수 설정’에서 로컬 수신 관련 옵션을 확인하고, 최종적으로는 실행 설정과 로그를 기준으로 판단하세요.
  4. UDP 확인: udp: true는 이 SOCKS 인바운드에서 UDP를 처리하도록 허용합니다. 다만 앱도 SOCKS UDP 방식을 사용해야 하며, 이후 아웃바운드와 네트워크 역시 해당 트래픽을 지원해야 합니다.

tag: socks-local은 인바운드의 내부 이름이며 도메인이나 구독 노드 이름이 아닙니다. 수신 프로토콜을 바꾸지는 않지만 다른 설정에서 이 인바운드를 참조할 수 있게 합니다. 이 예시의 첫 번째 규칙에서 inboundTag를 배열로 지정한 이유는 하나의 규칙이 여러 인바운드 태그를 받을 수 있기 때문입니다.

"inboundTag": [
  "socks-local"
]

인바운드 태그를 변경하면 해당 태그를 참조하는 규칙도 함께 수정해야 합니다. 인바운드의 socks-local만 local-socks로 바꾸고 규칙은 그대로 두면, 해당 규칙이 이 인바운드의 트래픽과 일치하지 않습니다.

outbounds: 태그, 프로토콜, 기본 아웃바운드

outbounds는 연결을 다음 단계로 어떻게 보낼지 정의합니다. 이 예시에서 freedom은 직결, blackhole은 차단을 뜻하며 원격 프록시 프로토콜이 아닙니다. 실제 구독에서 생성된 설정에는 VMess 또는 VLESS 아웃바운드가 추가되고 서버 주소, 포트, 사용자 ID, 전송 매개변수가 포함될 수 있습니다.

항목이 글의 예시확인할 점
tagdirect、block규칙에서 참조하는 이름으로, 대소문자와 철자가 정확히 일치해야 합니다.
protocol: freedom직결 아웃바운드코어가 실행 중인 기기에서 대상에 직접 연결합니다.
protocol: blackhole차단 아웃바운드지정된 트래픽을 거부하며 다른 아웃바운드로 자동 전환하지 않습니다.
VMess、VLESS이 예시에는 없음유효한 서버와 인증 매개변수가 필요하며 프로토콜 이름만 바꿔서는 사용할 수 없습니다.
streamSettings이 예시에는 필요 없음전송 방식과 보안 계층을 지정하며 서버 설정과 일치해야 합니다.

다른 기본 전달 방식이 설정되지 않은 이 예시에서는 라우팅 규칙에 일치하지 않는 연결이 outbounds의 첫 번째 항목을 사용하므로 기본적으로 직결됩니다. block을 첫 번째 항목으로 옮기면 기본 처리 방식이 바뀝니다. 태그 이름을 변경해도 배열 순서는 자동으로 바뀌지 않습니다.

핵심 정리: 기본 아웃바운드를 먼저 확인한 뒤 예외 규칙 살펴보기

실행 설정을 열고 먼저 outbounds 첫 번째 항목의 tag를 기록한 다음, routing.rules의 outboundTag를 하나씩 확인하세요. 태그 이름은 설정을 생성한 클라이언트가 정하므로 ‘proxy’를 검색하는 것만으로 확인하는 방법보다 확실합니다.

실제 설정에서 proxy를 프록시 아웃바운드 태그로 사용한다면 라우팅의 outboundTag: proxy가 일치하는 아웃바운드를 찾아야 합니다. 이 예시에 proxy를 가리키는 규칙을 추가해도 프록시 연결이 새로 생기지는 않습니다. 클라이언트가 생성한 유효한 프록시 아웃바운드를 유지하고 먼저 확인해야 합니다.

routing: 규칙은 위에서 아래로 확인하고 조건의 ‘AND’와 ‘OR’ 구분하기

routing.rules는 보통 위에서 아래 순서로 확인합니다. 규칙에 일치하면 지정된 아웃바운드를 사용하고 뒤에 있는 일반 규칙은 더 이상 확인하지 않습니다. 따라서 구체적인 규칙을 적용 범위가 넓은 규칙보다 앞에 배치해야 합니다. 설정 파일에 규칙이 있다고 해서 반드시 실행 기회를 얻는 것은 아닙니다.

하나의 규칙에서 서로 다른 조건 필드는 보통 ‘AND’로 결합되고, 같은 필드 배열에 있는 여러 후보 값은 보통 ‘OR’로 처리됩니다. 이 예시의 첫 번째 규칙은 인바운드가 socks-local이고 대상 도메인이 example.org여야 합니다. 두 조건을 모두 만족해야 block으로 전달됩니다.

로컬 SOCKS로 전달된 요청매칭 과정결과
도메인 example.org, TCP 443첫 번째 규칙의 인바운드와 정확한 도메인 조건을 모두 만족합니다.block
도메인 www.example.org, TCP 443정확한 도메인과 일치하지 않고 대상 포트도 25가 아닙니다.기본값 direct
기타 도메인, TCP 25첫 번째 규칙에는 일치하지 않지만 두 번째 규칙에는 일치합니다.block
기타 도메인, TCP 443두 규칙 모두 일치하지 않습니다.기본값 direct

위 예시는 앱이 도메인 이름을 SOCKS 인바운드에 전달한다는 전제에서 설명합니다. 앱이 미리 DNS 조회를 수행해 IP 주소만 전달하면 첫 번째 도메인 규칙을 적용하지 못할 수 있습니다. 스니핑은 일부 프로토콜에서 도메인 정보를 식별할 수 있지만 원래 도메인을 반드시 복원하는 기능은 아닙니다. 이 예시에서는 스니핑을 사용하지 않으므로 먼저 도메인이 명확한 요청으로 규칙을 테스트하세요.

설정 확인 절차와 자주 묻는 질문

설정을 읽을 때는 한 번에 변수 하나만 변경하고 수정 전후의 로그를 기록하세요. 먼저 코어가 시작되는지 확인하고, 다음으로 인바운드에 연결되는지, 마지막으로 규칙과 아웃바운드를 확인합니다. 포트와 DNS, 태그, 전송 매개변수를 한꺼번에 바꾸면 성공하거나 실패해도 원인을 파악하기 어렵습니다.

  1. 문법과 형식 확인: 포트 10808은 숫자이고, udp는 불리언 값 true이며, inbounds와 outbounds는 배열입니다.
  2. 모든 참조 확인: inboundTag와 outboundTag를 하나씩 찾아 해당 인바운드나 아웃바운드에 같은 이름의 tag가 있는지 확인합니다.
  3. 앱이 인바운드를 사용하는지 확인: 이 예시를 테스트할 때는 SOCKS 주소를 직접 지정해 브라우저의 시스템 프록시 설정이 판단에 영향을 주지 않도록 합니다.
  4. 라우팅 결과 확인: 첫 번째 규칙부터 대상 도메인, 대상 포트, 네트워크 종류, 인바운드 태그를 대조합니다.
  5. 클라이언트에서 관리하도록 복원: 원리를 확인한 뒤에는 클라이언트에서 지원하는 라우팅 또는 설정 옵션에 장기 변경 사항을 반영하고, 실행 설정을 다시 생성해 검증합니다.

기기에 SOCKS를 지원하는 curl이 설치되어 있고 코어가 이 예시를 불러왔으며 포트를 다른 프로세스가 사용하지 않는다면 아래 명령을 실행할 수 있습니다. socks5h는 대상 도메인을 SOCKS 서버에서 처리하도록 지정합니다. 이 예시에서는 정확한 도메인 규칙에 따라 요청이 차단되므로, 명령이 실패하는 것이 테스트의 예상 결과이며 원격 프록시 오류를 뜻하지 않습니다.

curl --proxy socks5h://127.0.0.1:10808 --connect-timeout 10 https://example.org/

JSON을 수정했는데 v2rayN을 다시 시작하면 왜 원래대로 돌아가나요?

클라이언트가 실행 파일을 다시 생성했을 수 있습니다. 수정 내역을 먼저 보관하고 ‘설정’ → ‘라우팅 설정’에서 해당 규칙을 확인하거나 현재 버전에서 공식 지원하는 사용자 지정 설정 메뉴를 사용하세요. 임시 실행 파일을 영구 설정의 원본으로 사용하지 마세요.

outboundTag에 proxy를 입력했는데 왜 시작 오류가 나나요?

outbounds 배열에서 tag가 proxy인 항목을 찾아 철자나 대소문자가 다른지 확인하세요. 해당 항목이 없다면 freedom의 이름만 바꿀 것이 아니라 유효한 프록시 아웃바운드 설정을 복원해야 합니다.

DNS를 1.1.1.1로 바꿨는데 도메인 규칙은 왜 적용되지 않나요?

먼저 앱이 도메인 이름을 전달하는지 확인한 다음 규칙 순서와 full, domain 접두사를 살펴보세요. DNS 서버를 바꿔도 앱이 이미 전달하지 않은 도메인 정보가 복구되지는 않으며 규칙 우선순위도 달라지지 않습니다.

VLESS 아웃바운드의 protocol 필드를 VMess에서 바로 바꿔도 되나요?

안 됩니다. 두 프로토콜은 인증 필드와 매개변수가 다르며 전송 방식과 보안 계층도 서버 설정에 맞아야 합니다. 올바른 구독이나 연결 정보를 다시 가져온 뒤 현재 실행 중인 코어에서 지원하는 필드를 확인하세요.

라우팅 개념을 더 알아보려면 라우팅 분할 기술 참고 자료를 확인하세요. 클라이언트 화면에서 DNS 설정을 살펴보려면 가이드의 DNS 설정 절차를 따르세요. 코어 구조를 이해한 다음 클라이언트 화면과 항목별로 대조하면 구독, 인바운드, 라우팅을 같은 설정으로 혼동하는 일을 줄일 수 있습니다.

클라이언트 다운로드 및 설치v2rayN · v2rayNG