고급 활용 예상 읽기 시간 22분

Xray JSON 설정 심화: 모듈형 라우팅과 운영 구성

Xray-core의 JSON 설정을 인바운드, 아웃바운드, 라우팅, DNS, 정책, Observatory 구조로 나누어 살펴봅니다. 라우팅 규칙 우선순위와 DNS 누수 방지, 설정 파일 분리, Git 버전 관리, 보안 정보 보호를 실행 가능한 예제와 함께 설명해 유지보수와 자동 배…

Xray의 JSON 설정은 서버 한 대의 접속 정보를 적어 두는 파일을 넘어, 인바운드·아웃바운드·DNS·라우팅·로그를 하나의 실행 정책으로 연결하는 운영 문서에 가깝습니다. 설정 항목이 늘어날수록 모든 내용을 하나의 config.json에 계속 추가하는 방식은 빠르게 한계에 도달합니다. 어떤 포트가 열려 있는지, DNS 요청이 어느 경로로 나가는지, 규칙이 어떤 순서로 매칭되는지 확인하기 어려워지고, 작은 쉼표 오류 하나가 전체 코어의 시작 실패로 이어질 수 있습니다.

실전에서는 먼저 기능을 역할별로 나눈 뒤, 시작 전에 최종 JSON을 생성하거나 검증하는 구조가 관리하기 좋습니다. 다만 Xray가 임의의 conf.d 디렉터리를 자동으로 병합한다고 가정해서는 안 됩니다. 실행 방식과 코어 버전에 따라 읽는 파일은 시작 인자로 지정된 단일 설정일 수 있으므로, 모듈형 파일을 사용한다면 명시적인 병합 절차와 검증 절차를 함께 마련해야 합니다. 이 글에서는 Xray-core 25.x 계열을 기준으로, JSON 구조를 분리하고 라우팅과 DNS를 연결하며 장애를 추적하는 방법을 설명합니다.

本文速览

인바운드, 아웃바운드, DNS, 라우팅, 로그를 독립적인 모듈로 설계하고 최종 실행 파일에서 결합하는 방법을 다룹니다. v2rayN에서 사용하는 로컬 포트와 Xray 단독 실행 환경을 함께 고려하므로, 설정을 자주 수정하는 개발자와 네트워크 엔지니어가 운영 장애의 범위를 빠르게 좁히는 데 도움이 됩니다.

5개
핵심 구성 모듈
10808
권장 HTTP 포트 예시
10809
권장 SOCKS 포트 예시
3단계
운영 로그 확인 순서

JSON 설정을 운영 계약으로 설계하기

Xray 설정을 작성할 때 가장 먼저 해야 할 일은 필드 목록을 외우는 것이 아니라 트래픽의 생명주기를 그리는 것입니다. 애플리케이션이나 LAN 장치의 연결이 인바운드에 들어오고, DNS 정보와 대상 주소가 준비되며, 라우팅 규칙이 아웃바운드를 선택한 뒤, 해당 아웃바운드가 직접 연결 또는 프록시 연결을 만듭니다. 로그는 이 과정에서 실제로 어떤 분기가 선택되었는지 확인하는 증거가 됩니다.

애플리케이션 요청인바운드 수신DNS 처리라우팅 매칭아웃바운드 전송로그 기록

최상위 JSON에는 일반적으로 log, api, inbounds, outbounds, dns, routing, policy, stats 같은 영역이 들어갑니다. 모든 환경에서 모든 영역이 필요한 것은 아닙니다. 예를 들어 단순한 데스크톱 프록시는 API와 통계 기능 없이도 동작할 수 있지만, 운영 중인 게이트웨이에서는 상태 확인과 장애 분석을 위해 로그와 통계 모듈을 별도로 고려하는 편이 좋습니다.

모듈을 나눌 때는 기능뿐 아니라 변경 빈도도 기준으로 삼으세요. 인바운드의 로컬 포트는 자주 바뀌지 않지만, 라우팅 예외와 도메인 목록은 자주 변경될 수 있습니다. 노드가 구독으로 갱신되는 환경에서는 아웃바운드 모듈의 변경 주기가 짧을 수 있고, DNS 정책은 네트워크 환경을 바꿀 때만 수정될 수 있습니다. 변경 빈도가 다른 내용을 한 파일에서 관리하면 검토 범위가 불필요하게 커집니다.

모듈 파일 배치와 최종 검증

운영 환경에서는 실행 파일, 원본 모듈, 생성된 최종 설정, 로그를 구분하는 것이 좋습니다. 다음은 Linux 서버나 보조 라우터에서 사용할 수 있는 한 가지 예시입니다. 디렉터리 이름 자체가 Xray의 고정 규칙은 아니며, 서비스 파일이나 컨테이너 시작 명령에서 실제 경로를 명확히 지정해야 합니다.

/etc/xray/
├── modules/
│   ├── 10-inbounds.json
│   ├── 20-outbounds.json
│   ├── 30-dns.json
│   ├── 40-routing.json
│   └── 50-policy.json
├── generated/
│   └── config.json
└── secrets/
    └── node-values.json

/var/log/xray/
├── access.log
└── error.log

인바운드 모듈에는 로컬 HTTP 프록시와 SOCKS 프록시를 분리해 둘 수 있습니다. 예를 들어 HTTP는 127.0.0.1:10808, SOCKS는 127.0.0.1:10809에 바인딩하면 v2rayN이나 다른 로컬 프로그램이 역할에 맞는 포트를 선택할 수 있습니다. 외부 LAN 장치가 직접 접근해야 하는 경우에는 127.0.0.1 대신 내부 인터페이스 주소를 사용해야 하지만, 그때는 방화벽과 인증, 접근 가능한 서브넷을 함께 설정해야 합니다.

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

모듈을 결합한 뒤에는 반드시 JSON 문법 검사를 먼저 실행하고, 그 다음 Xray 자체 설정 검사를 수행해야 합니다. 일반 JSON 파서는 괄호와 쉼표 오류는 찾지만, 존재하지 않는 필드나 서로 맞지 않는 프로토콜 조합까지 완전히 검증하지는 못합니다. 서비스 재시작 전에 최종 파일이 비어 있지 않은지, 예상한 인바운드 태그와 아웃바운드 태그가 모두 포함되었는지 확인하세요.

  1. 원본 백업

    현재 정상 동작하는 config.json을 날짜와 변경 목적을 포함한 별도 사본으로 보존합니다.

  2. 모듈 수정

    인바운드, DNS, 라우팅처럼 하나의 역할만 담당하는 파일을 수정하고 다른 모듈은 동시에 바꾸지 않습니다.

  3. 최종 병합

    정해 둔 생성 절차로 generated/config.json을 만들고 배열 항목이 누락되지 않았는지 비교합니다.

  4. 코어 검사

    xray run -test -config /etc/xray/generated/config.json과 같은 방식으로 실행 전 검증을 수행합니다.

  5. 단계적 적용

    서비스를 다시 시작한 뒤 로컬 포트, DNS, 테스트 도메인 순서로 기능을 확인합니다.

DNS와 라우팅을 하나의 정책으로 연결하기

라우팅 규칙과 DNS를 따로 작성하면 설정은 동작하더라도 의도하지 않은 조회 경로가 생길 수 있습니다. 예를 들어 특정 도메인을 프록시로 보낼 계획인데 로컬 DNS가 먼저 해당 도메인을 조회하면, 질의 정보가 직접 연결 경로로 빠질 수 있습니다. 반대로 모든 DNS를 원격으로 보낼 경우에는 로컬 장치 이름이나 내부 도메인을 해석하지 못할 수 있습니다. 따라서 도메인 정책, 조회 서버, 최종 아웃바운드의 관계를 먼저 정의해야 합니다.

직접 연결 DNS

서버
223.5.5.5
용도
직접 연결 대상
태그
dns-direct

로컬 정책과 허용된 도메인의 주소를 확인하는 경로입니다.

프록시 DNS

서버
https+local://1.1.1.1/dns-query
용도
프록시 대상
태그
dns-proxy

원격 경로에서 처리할 도메인의 조회 위치를 분리합니다.

실제 서버 주소와 DNS 주소는 사용 중인 네트워크 정책에 맞게 선택해야 합니다. 위 값은 구조를 설명하기 위한 예시이며, 특정 DNS 서버가 모든 환경에서 접근 가능하다는 뜻은 아닙니다. DNS 모듈에서는 도메인 suffix, geosite 분류, 예상 네트워크 대역을 기준으로 서버를 선택할 수 있습니다. 중요한 점은 DNS 규칙의 순서와 라우팅 규칙의 순서가 서로 모순되지 않는 것입니다.

라우팅은 일반적으로 다음 순서가 안전합니다. 첫째, geoip:private와 내부 도메인을 직접 연결로 보냅니다. 둘째, 관리 목적의 로컬 서비스와 운영용 예외를 별도 규칙으로 처리합니다. 셋째, 직접 연결할 지역 도메인이나 주소를 배치합니다. 마지막에 그 외 TCP와 UDP를 프록시 아웃바운드로 보내는 기본 규칙을 둡니다. Xray 라우팅은 규칙을 위에서 아래로 평가하므로 기본 규칙을 앞에 두면 뒤의 예외 규칙이 실행되지 않습니다.

{
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      {
        "type": "field",
        "ip": ["geoip:private"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "domain": ["geosite:cn"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "network": "tcp,udp",
        "outboundTag": "proxy"
      }
    ]
  }
}

domainStrategy는 DNS와 라우팅의 결합 방식에 영향을 주므로 환경에 맞게 선택해야 합니다. 도메인 정보가 인바운드에서 보존되는지, 대상이 이미 IP인지, 코어가 라우팅 전에 주소를 해석해야 하는지에 따라 결과가 달라집니다. FakeDNS나 TUN을 사용하는 경우에는 가상 주소 대역이 사설 주소 규칙에 잘못 걸려 직접 연결되는지 확인해야 합니다. 예약된 FakeDNS 대역을 일반적인 내부망 예외와 똑같이 취급하면 프록시 대상이 로컬로 빠지는 문제가 생길 수 있습니다.

결론: DNS와 라우팅은 함께 테스트해야 합니다

도메인 하나를 추가한 뒤 웹페이지가 열리는지만 확인하지 말고, DNS 질의 경로와 최종 아웃바운드 태그를 각각 확인하세요. 같은 도메인이 직접 연결, 프록시, FakeDNS 환경에서 서로 다른 결과를 낼 수 있으므로 정책 변경 후에는 도메인 규칙과 IP 규칙을 함께 점검해야 합니다.

아웃바운드 태그와 프로토콜 매개변수 관리

라우팅 모듈은 아웃바운드의 실제 접속 세부 정보보다 outboundTag를 기준으로 대상을 선택합니다. 따라서 태그 이름은 모듈 사이의 인터페이스입니다. 라우팅에서 proxy를 지정했는데 아웃바운드 태그가 proxy-main이면 JSON 문법은 정상이어도 해당 연결을 찾지 못합니다. direct, proxy, block처럼 역할을 나타내는 태그를 사용하고, 노드가 여러 개라면 proxy-seoul, proxy-backup처럼 목적을 구분하는 이름을 지정하세요.

모듈확인할 키대표 장애점검 방법
인바운드tag, listen, port포트 충돌, 잘못된 바인딩포트 점유 상태와 로컬 요청 확인
아웃바운드protocol, 서버 주소, 포트핸드셰이크 실패, 인증 실패서버 측 프로토콜과 항목별 대조
스트림전송 방식, TLS, SNI, flow연결은 되지만 세션 수립 실패서버 설정과 전송 계층 비교
라우팅outboundTag, 규칙 순서원하는 노드가 선택되지 않음첫 매칭 규칙과 태그 존재 여부 확인
DNS서버, queryStrategy, 규칙도메인 해석 실패 또는 DNS 유출질의 로그와 실제 연결 경로 비교

VLESS, VMess, Trojan 등 프로토콜을 바꿀 때는 주소와 포트만 수정해서는 안 됩니다. 사용자 식별자, 보안 계층, 전송 방식, 서버 이름, 지문, flow와 같은 항목이 서버 설정과 일치해야 합니다. 특히 VLESS와 VMess는 서로 다른 프로토콜이므로 VMess 전용 필드를 VLESS에 옮겨 적거나, VLESS의 flow 값을 다른 전송 방식에 그대로 넣는 방식은 유효한 설정이 아닙니다. 구독에서 가져온 노드를 JSON으로 옮길 때도 필드를 추측하지 말고 원본 설정을 항목별로 대조하세요.

로그를 이용한 운영 장애 추적

로그는 많이 남긴다고 항상 좋은 것이 아닙니다. 운영 초기에는 info 수준으로 코어 시작, 인바운드 초기화, DNS 오류, 아웃바운드 연결 실패를 확인하고, 문제가 재현되지 않을 때는 warning 수준으로 낮추는 방식이 실용적입니다. debug 로그는 짧은 재현 구간에서만 사용하세요. 장시간 활성화하면 로그 파일이 빠르게 커지고, 실제 오류 사이에 반복적인 연결 정보가 묻힐 수 있습니다.

{
  "log": {
    "loglevel": "warning",
    "access": "/var/log/xray/access.log",
    "error": "/var/log/xray/error.log"
  }
}

장애 분석은 세 단계로 나누면 효율적입니다. 첫째, 애플리케이션이 로컬 인바운드 포트에 요청을 보냈는지 확인합니다. 둘째, 라우팅이 예상한 아웃바운드 태그를 선택했는지 확인합니다. 셋째, 선택된 아웃바운드가 DNS 조회와 원격 핸드셰이크를 성공했는지 확인합니다. 첫 단계부터 기록이 없다면 원격 노드를 의심하기보다 시스템 프록시, TUN, 애플리케이션의 프록시 사용 여부를 먼저 봐야 합니다.

connection refused는 보통 대상 포트에 프로세스가 없거나 방화벽이 적극적으로 거부한 경우에 나타납니다. i/o timeout은 경로 차단, 잘못된 주소, 응답 지연 등 여러 원인이 있으므로 단독으로 프로토콜 오류라고 단정할 수 없습니다. failed to find an available destination은 라우팅 결과나 대상 주소 해석이 정상적으로 구성되지 않았을 가능성을 확인해야 합니다. 같은 시간에 모든 아웃바운드가 실패하면 DNS, 기본 네트워크, 시스템 시간, 코어 실행 상태처럼 공통 계층을 우선 점검하세요.

报错: connection refused

원인과 해결: 로컬 또는 원격 포트에서 연결을 받는 프로세스가 없을 수 있습니다. 먼저 10808, 10809 포트 점유 상태와 아웃바운드의 원격 포트를 각각 확인하세요.

报错: i/o timeout

원인과 해결: DNS 해석, 네트워크 경로, 방화벽 또는 핸드셰이크 지연이 원인일 수 있습니다. 도메인 해석 결과와 IP 직접 연결 결과를 분리해 테스트하세요.

报错: failed to find an available destination

원인과 해결: 라우팅 결과가 유효한 아웃바운드로 이어지지 않거나 대상 주소를 만들지 못한 상태입니다. 규칙의 outboundTag, 태그 이름, DNS 설정을 함께 대조하세요.

报错: failed to load config

원인과 해결: 최종 JSON의 문법 오류, 잘못된 경로 또는 지원되지 않는 필드일 수 있습니다. 원본 모듈이 아니라 실제 서비스가 읽는 최종 파일을 대상으로 JSON 파서와 Xray 검사를 차례로 실행하세요.

운영 중 설정을 변경할 때는 한 번에 하나의 모듈만 수정하고, 변경 전후에 같은 테스트 대상을 사용하세요. 예를 들어 example.net의 DNS 해석, HTTP 요청, 프록시 로그를 차례로 확인하면 라우팅 변경이 실제로 적용되었는지 비교하기 쉽습니다. 설정을 재생성하는 자동화 과정이 있다면 생성 시각, 입력 모듈 목록, 최종 파일 경로를 로그에 남기고, 비어 있는 모듈이나 중복 포트가 발견되면 배포를 중단하도록 구성하세요.

안전한 배포와 유지 관리 체크리스트

모듈형 구성의 목적은 파일을 많이 만드는 데 있지 않습니다. 핵심은 변경 범위를 줄이고, 최종 결과를 사람이 검토할 수 있게 하며, 문제가 발생했을 때 이전 상태로 돌아갈 수 있도록 하는 것입니다. 새 노드를 추가할 때는 먼저 아웃바운드만 검증하고, 그 다음 라우팅 예외를 추가하세요. DNS 정책을 바꿀 때는 기존 직접 연결 대상과 내부 도메인이 영향을 받지 않는지 확인해야 합니다.

v2rayN에서 사용하는 코어 설정이라면 먼저 클라이언트가 생성한 기본 구성을 안정적으로 실행한 뒤, 필요한 라우팅과 DNS 항목만 단계적으로 조정하는 편이 안전합니다. 코어를 별도 서비스로 운영하는 경우에는 서비스 관리자가 실제로 어떤 경로의 JSON을 읽는지, 재시작 시 생성 파일이 먼저 준비되는지 확인하세요. 설정 파일이 정확해도 서비스가 오래된 파일을 읽고 있다면 수정 사항이 반영되지 않은 것처럼 보일 수 있습니다.

결국 좋은 Xray JSON은 가장 짧은 설정이 아니라, 역할과 의존 관계가 분명하고 변경 결과를 검증할 수 있는 설정입니다. 인바운드가 어디에서 트래픽을 받는지, DNS가 어느 경로로 이름을 해석하는지, 라우팅이 어떤 순서로 태그를 선택하는지, 로그에서 각 단계의 결과를 어떻게 확인할지 문서화하면 노드 교체나 정책 변경이 훨씬 안전해집니다. 모듈을 나누되 최종 파일은 항상 하나의 실행 가능한 정책으로 검증하고, 장애가 발생하면 가장 바깥 계층부터 안쪽 계층으로 범위를 좁히는 것이 운영의 기본 원칙입니다.

v2rayN 다운로드