Xray 설정 파일 구조 해설: inbounds·outbounds·routing 한눈에 보기

log, inbounds, outbounds, routing, dns 순서로 Xray JSON 설정을 살펴보고 각 항목의 역할과 트래픽 흐름을 알아봅니다.

이 글의 핵심

노드 가져오기는 해봤고 Xray 기본 설정 파일을 읽어보려는 사용자를 위한 글입니다. 로컬 인바운드와 직접 연결·차단 아웃바운드만 포함한 완전한 JSON을 기준으로 삼아 로그, 포트, 태그, 라우팅 순서와 DNS의 관계를 설명합니다. 글을 읽고 나면 트래픽이 어느 단계에서 막혔는지 찾고, 클라이언트 설정과 공유 링크, 코어 실행 설정의 역할을 구분할 수 있습니다.

설정 객체와 트래픽 흐름

Xray는 연결을 처리하는 코어이고, v2rayN은 노드를 관리하고 설정을 생성해 코어를 실행하는 데스크톱 클라이언트입니다. 노드 공유 링크에는 보통 원격 연결 하나만 기술되지만, 기본 JSON 설정에는 로컬 리스너와 여러 아웃바운드, 라우팅, DNS를 함께 지정할 수 있습니다. 공유 링크를 config.json으로 저장해도 실행 가능한 코어 설정으로 자동 변환되지는 않습니다.

앱 요청 인바운드 수신 라우팅 규칙 적용 아웃바운드 연결 대상 서버

위 흐름을 중심으로 설정을 읽으면 됩니다. log는 상태를 기록하고, dns는 도메인 이름을 확인해야 할 때 처리에 관여합니다. 둘 다 모든 요청이 차례로 거치는 별도의 중계 단계는 아닙니다. JSON에서 최상위 객체를 작성한 순서도 트래픽 흐름을 결정하지 않습니다.

전체 JSON 예제: 로컬 인바운드와 직접 연결 기본 설정

아래는 설정 구조를 이해하기 위한 독립 실행형 설정입니다. SOCKS 인바운드는 127.0.0.1:10808, HTTP 인바운드는 127.0.0.1:10809에서 수신 대기합니다. 원격 프록시 노드는 포함하지 않습니다. 일반 요청은 직접 연결하고, 테스트용 예약 도메인인 blocked.test로 향하는 요청은 차단합니다. 원격 프록시의 연결 가능 여부를 확인하는 용도로는 사용할 수 없습니다.

{
  "log": {
    "loglevel": "warning"
  },
  "inbounds": [
    {
      "tag": "socks-in",
      "listen": "127.0.0.1",
      "port": 10808,
      "protocol": "socks",
      "settings": {
        "auth": "noauth",
        "udp": true
      }
    },
    {
      "tag": "http-in",
      "listen": "127.0.0.1",
      "port": 10809,
      "protocol": "http",
      "settings": {}
    }
  ],
  "outbounds": [
    {
      "tag": "direct",
      "protocol": "freedom",
      "settings": {
        "domainStrategy": "UseIP"
      }
    },
    {
      "tag": "block",
      "protocol": "blackhole",
      "settings": {}
    }
  ],
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      {
        "type": "field",
        "domain": [
          "full:blocked.test"
        ],
        "outboundTag": "block"
      },
      {
        "type": "field",
        "ip": [
          "127.0.0.0/8",
          "10.0.0.0/8",
          "172.16.0.0/12",
          "192.168.0.0/16"
        ],
        "outboundTag": "direct"
      }
    ]
  },
  "dns": {
    "servers": [
      "localhost"
    ]
  }
}

예제는 엄격한 JSON 형식입니다. 키와 문자열은 큰따옴표로 감싸고 주석은 넣지 않으며, 배열의 마지막 항목 뒤에 쉼표를 붙이지 않습니다. 뒤에 나오는 항목 설명은 이 설정을 이해하기 위한 내용이므로, 각 부분을 다시 조합해 설정 파일을 하나 더 만들 필요는 없습니다.

log: 오류가 발생한 위치부터 확인하기

loglevel은 오류 로그의 상세 수준을 설정합니다. 흔히 쓰는 값은 debug, info, warning, error, none입니다. 문제를 확인할 때는 예제의 warning을 잠시 info로 바꿔 볼 수 있습니다. 점검이 끝나면 연결 세부 정보가 지나치게 기록되지 않도록 원래 값으로 되돌리세요.

항목용도점검할 내용
loglevel오류 로그 수준 조정설정 불러오기, 리스너 시작 실패, 연결 실패에 관한 구체적인 메시지
access액세스 로그 파일 지정요청 대상과 실제로 사용된 아웃바운드 정보
error오류 로그 파일 지정시작 및 실행 중 오류의 맥락

이 예제에서는 로그 파일 경로를 지정하지 않았으므로 명령줄에서 실행할 때 먼저 콘솔 출력을 확인하세요. access나 error를 추가한다면 현재 프로세스가 쓸 수 있는 경로를 지정하고, 상대 경로인지 실행 작업 디렉터리 기준인지 구분해야 합니다. 로그에 접속 대상이 기록될 수 있으므로 문제 해결 내용을 공유하기 전에 민감한 정보를 삭제하세요.

inbounds: 수신 주소, 포트, 인바운드 태그

인바운드 객체 하나가 트래픽을 받는 입구 하나에 해당합니다. listen은 바인딩 주소, port는 수신 대기 포트, protocol은 앱이 연결할 프로토콜을 지정합니다. tag는 설정 내부에서 사용하는 식별자입니다. 비밀번호도 아니고 브라우저에 입력하는 호스트 이름도 아닙니다.

SOCKS 인바운드

태그
socks-in
주소
127.0.0.1
포트
10808
프로토콜
socks
UDP
true

앱에서 SOCKS 프로토콜로 연결해야 합니다. UDP를 사용하려면 앱과 이후 연결 경로에서도 모두 UDP를 지원해야 합니다.

HTTP 인바운드

태그
http-in
주소
127.0.0.1
포트
10809
프로토콜
http
프로토콜 설정
{}

HTTPS 사이트는 보통 HTTP CONNECT로 터널을 만듭니다. 인바운드 프로토콜을 HTTPS로 바꾸는 방식이 아닙니다.

127.0.0.1은 로컬 기기에서 오는 연결만 받습니다. 이를 0.0.0.0으로 바꾸면 IPv4 수신 범위가 넓어지며, 다른 기기에서 실제로 접속할 수 있는지는 방화벽과 네트워크 환경에 따라 달라집니다. 이 예제의 SOCKS는 noauth를 사용하므로, LAN 공유를 시도하려고 수신 범위를 바로 넓히지 마세요.

outbounds: 아웃바운드 프로토콜과 기본 연결

outbounds는 라우팅에서 선택할 수 있는 아웃바운드 배열입니다. 이 예제의 freedom은 대상에 직접 연결하고 blackhole은 연결을 차단합니다. 여기서 direct와 block은 임의로 정한 태그입니다. 실제 동작을 결정하는 것은 태그의 영문 뜻이 아니라 protocol입니다.

설정 계층예제 항목담당 기능
아웃바운드 식별자tag라우팅에서 참조하는 outboundTag
프록시 프로토콜protocolfreedom, blackhole, VMess, VLESS, Trojan, Shadowsocks 등에 해당하는 프로토콜 구현
프로토콜 매개변수settings아웃바운드 프로토콜에 따라 대상, 사용자 또는 기타 프로토콜 설정 지정
전송 매개변수streamSettings지원되는 프록시 아웃바운드의 전송 방식과 전송 보안 설정

원격 프록시 아웃바운드를 추가할 때는 프로토콜, 서버 주소, 포트, 인증 정보, 전송 설정이 서로 일치해야 합니다. freedom을 vless로 바꾸는 것만으로 노드 설정이 완료되는 것은 아닙니다. TLS 또는 REALITY 관련 항목은 해당 전송 보안 설정에 속하므로 SOCKS 인바운드의 settings에 임의로 넣지 마세요.

정리: 아웃바운드 순서를 바꾸기 전에 기본 연결 규칙부터 정하세요

어떤 라우팅 규칙에도 일치하지 않으면 일반적으로 첫 번째 아웃바운드를 사용합니다. 이 예제에서는 direct가 첫 번째이므로 일치하는 규칙이 없는 요청은 직접 연결됩니다. 기본 프록시 연결을 원한다면 기본 아웃바운드를 명확하게 구성하세요. 특정 규칙에 프록시 태그가 있는지만 확인해서는 안 됩니다.

routing: 규칙 순서, 태그 참조, DNS 해석 전략

routing.rules는 배열 순서대로 확인하며, 처음 일치하는 규칙이 아웃바운드를 결정합니다. 이 예제에서는 첫 번째 규칙이 전체 도메인 blocked.test를 block으로 보내고, 두 번째 규칙이 지정된 IPv4 대역을 direct로 보냅니다. 예시 대역은 일부일 뿐 모든 특수 주소를 포함하지 않으며 IPv6도 다루지 않습니다.

  1. 요청이 들어오면 코어는 인바운드 태그, 대상 도메인 또는 IP, 포트 등 확인 가능한 정보를 가져옵니다.
  2. 첫 번째 규칙부터 차례대로 확인합니다. 한 규칙 안에서 조건 유형이 서로 다르면 일반적으로 모든 조건을 충족해야 합니다.
  3. 같은 유형의 목록에 있는 항목은 보통 하나만 일치해도 됩니다. 예를 들어 ip 배열의 대역 중 하나와 일치하면 됩니다.
  4. 일치하는 규칙을 찾으면 해당 규칙의 outboundTag를 사용합니다. 일치하는 규칙이 없으면 기본 아웃바운드로 연결합니다.

full:은 도메인 전체를 정확히 일치시키고, domain:은 도메인과 하위 도메인에 일치합니다. 특정 인바운드에만 규칙을 적용하려면 inboundTag 배열을 추가할 수 있습니다. 참조하는 태그는 해당 객체의 tag와 일치해야 합니다. 인바운드 태그와 아웃바운드 태그의 용도는 다르므로 socks-in을 아웃바운드로 참조할 수 없습니다.

routing.domainStrategy도메인 해석 시점라우팅 규칙을 읽을 때 주의할 점
AsIs라우팅 일치를 위해 도메인을 먼저 해석하지 않음도메인 대상은 IP 규칙만으로 분기된다고 볼 수 없음
IPIfNonMatch도메인 규칙이 일치하지 않으면 이후 해석을 거쳐 다시 일치 여부 확인앞에서 너무 광범위한 규칙이 일치하면 이후 해석 및 규칙 검사가 진행되지 않음
IPOnDemandIP 정보가 필요한 규칙을 검사하는 단계에서 DNS 해석 시작IP 규칙의 위치에 따라 DNS 해석 시점이 달라짐

이 예제는 AsIs를 선택했으므로 도메인으로 전달된 요청은 두 번째 IP 규칙과 일치시키기 위해 해석되지 않습니다. 다만 기본 아웃바운드가 직접 연결이므로 요청이 직접 연결된다는 사실만으로 사설 네트워크 대역 규칙에 일치했다고 판단할 수는 없습니다. 규칙을 점검할 때는 페이지가 열리는지만 볼 것이 아니라 실제로 어떤 규칙과 아웃바운드가 선택됐는지 확인하세요.

dns: 라우팅을 위한 해석과 연결을 위한 해석 구분하기

dns를 설정해도 모든 앱의 DNS 요청을 가로채는 것은 아닙니다. 앱이 먼저 직접 도메인을 해석한 뒤 IP를 프록시에 전달할 수도 있고, 도메인을 SOCKS나 HTTP 인바운드에 넘길 수도 있습니다. 라우팅에서 어떤 정보를 사용할 수 있는지는 우선 인바운드가 실제로 무엇을 받았는지에 따라 달라집니다.

이 예제의 dns.servers에는 특수 값인 localhost를 사용합니다. 이는 로컬 시스템의 DNS 해석기를 통해 도메인을 해석한다는 뜻이지, 로컬에서 실행 중인 DNS 서버를 지정하는 것이 아닙니다. UDP 53 포트의 리스너도 생성하지 않습니다. 앱에 DNS 서비스를 제공하려면 DNS 요청을 받는 인바운드와 처리 경로를 별도로 구성해야 합니다.

이 예제는 freedom 아웃바운드에서 UseIP를 설정해 직접 연결할 때 내장 DNS 해석을 사용합니다. 라우팅의 AsIs와 충돌하지 않습니다. 앞의 설정은 아웃바운드가 연결을 준비할 때 적용되고, 뒤의 설정은 라우팅 규칙을 확인하는 단계에 적용됩니다. 아웃바운드가 나중에 IP를 얻더라도 이미 끝난 라우팅 선택을 자동으로 다시 실행하지는 않습니다.

정리: 앱이 도메인과 IP 중 무엇을 전달하는지 먼저 확인하세요

도메인 규칙이 일치하지 않으면 앱이 로컬에서 이미 도메인을 해석했는지 먼저 확인하세요. 라우팅 전략과 DNS 서버, 아웃바운드 프로토콜을 한꺼번에 바꾸지 마세요. 한 번에 한 계층만 변경해야 결과가 달라진 원인을 찾을 수 있습니다.

검증 및 문제 해결: 설정 확인부터 실제 연결까지

먼저 코어에 포함된 검사 명령으로 설정을 검증하고, 다음으로 리스너를 시작한 뒤 앱을 연결하세요. 아래 명령은 터미널에서 xray 실행 파일을 찾을 수 있을 때 사용할 수 있습니다. 명령 검색 경로에 파일이 없다면 실제 파일 경로를 지정하세요. 버전과 도움말을 먼저 확인하면 현재 실행 파일에서 지원하는 옵션을 알 수 있습니다.

xray version
xray run -help
xray run -test -config ./config.json
xray run -config ./config.json

설정 검사가 통과했다는 것은 코어가 설정을 받아들였다는 뜻일 뿐, 원격 서버에 연결할 수 있거나 앱이 프록시를 사용한다는 보장은 아닙니다. 같은 조건으로 로컬 테스트를 반복하려면 다른 터미널에서 테스트 서버를 시작한 다음 두 인바운드를 통해 각각 접속해 보세요:

python -m http.server 8000 --bind 127.0.0.1
curl --noproxy "" --proxy socks5h://127.0.0.1:10808 http://127.0.0.1:8000/
curl --noproxy "" --proxy http://127.0.0.1:10809 http://127.0.0.1:8000/

테스트하려면 로컬에 Python과 curl이 설치되어 있어야 합니다. 응답은 로컬 테스트 디렉터리에서 제공되며, “앱의 인바운드 연결, 라우팅 선택, 직접 연결 아웃바운드” 경로를 확인합니다. --noproxy ""는 기존 우회 설정 때문에 이번 인바운드 테스트가 건너뛰어지는 것을 방지합니다. 테스트가 끝나면 임시 서버를 중지하세요. 터미널에 표시된 디렉터리 목록은 프록시 노드의 속도 테스트 결과가 아닙니다.

설정 검사는 통과했는데 시작하자마자 종료되는 이유는 무엇인가요?

콘솔에 리스너 시작 실패 메시지가 있는지 확인하세요. 10808과 10809를 다른 프로세스가 사용하지 않는지 확인하고, 필요하면 11808과 11809로 바꾼 뒤 앱의 프록시 포트도 함께 변경하세요.

도메인 규칙을 추가했는데도 기본 아웃바운드로 연결되는 이유는 무엇인가요?

요청에 도메인이 포함되어 있는지 먼저 확인한 뒤, 앞쪽의 일반 규칙이 요청을 먼저 가로채는지 살펴보세요. full:의 전체 일치 범위와 outboundTag가 올바른 아웃바운드를 참조하는지도 확인하세요.

v2rayN에서 실행 파일을 수정했는데 재시작 후 변경 사항이 사라진 이유는 무엇인가요?

클라이언트가 실행 설정을 다시 생성했을 수 있습니다. 별도 사본을 먼저 보관한 뒤 현재 버전에서 제공하는 사용자 지정 설정 기능으로 불러오거나, 클라이언트의 해당 라우팅·DNS 설정에서 수정하세요. 임시 실행 파일을 장기 설정의 원본으로 사용하지 마세요.

이 JSON을 모든 클라이언트에 그대로 가져올 수 있나요?

가져오기 기능이 기본 설정을 받는지 노드 공유 정보를 받는지 먼저 확인하세요. v2rayNG는 Xray 코어를 사용하고 v2flyNG는 v2fly 코어를 사용합니다. 전체 설정의 호환성은 코어 버전과 지원 항목, 클라이언트의 불러오기 방식에 따라 달라집니다.

기본 설정 검증을 마친 다음 원격 프록시 아웃바운드를 추가하고, 사용할 라우팅 규칙을 명확히 지정하세요. 한 번에 한 부분만 수정하고 정상 작동하는 이전 설정을 보관하세요. 문제가 생기면 여러 관련 없는 설정을 번갈아 바꾸는 대신 원인을 인바운드, 규칙, DNS 해석, 원격 연결 중 하나로 좁힐 수 있습니다.

v2rayN 다운로드