클라이언트에 설정을 가져온 뒤, 실제로 실행되는 코어 설정을 이해하고 싶은 사용자를 위한 글입니다. 로컬 인바운드, 직접 연결 및 차단 아웃바운드만 포함한 완성된 JSON으로 기본 구조를 살펴본 다음 tag 참조, 라우팅 순서, DNS의 관계를 확인합니다. 예시에는 원격 프록시 서버가 없습니다. 글을 읽고 나면 요청이 어디로 들어와 어떤 규칙에 따라 처리되는지, 설정을 수정한 뒤 어떤 필드를 확인해야 하는지 파악할 수 있습니다.
1. 클라이언트 설정과 코어 설정 구분하기
v2rayN, v2rayNG, v2flyNG는 클라이언트이지 코어 자체가 아닙니다. 클라이언트는 구독, 노드, 사용자 인터페이스를 관리하고 관련 설정을 코어가 읽을 수 있는 형식으로 변환합니다. v2rayNG는 Xray 코어를, v2flyNG는 v2fly 코어를 사용합니다. v2rayN의 실행 설정은 현재 선택된 코어를 확인해야 합니다. 코어와 버전에 따라 지원하는 필드가 다르므로 서로 같은 것으로 간주하면 안 됩니다.
이 글에서는 V2Ray의 전통적인 JSON 설정 구조에서 자주 쓰이는 필드를 다룹니다. 실제 설정을 확인할 때는 먼저 코어 설정인지 클라이언트 자체 설정 파일인지 구분하세요. 인터페이스에 저장된 구독 그룹, 창 옵션, 업데이트 설정은 코어의 config.json에 포함되지 않을 수 있습니다. 반대로 실행 설정의 로컬 포트는 클라이언트가 자동으로 생성했을 수도 있습니다.
이 흐름은 로컬 프록시를 명시적으로 사용하는 앱을 기준으로 합니다. 코어를 실행하는 것만으로 모든 프로그램이 로컬 포트에 자동으로 연결되지는 않습니다. 시스템 프록시, 앱 내 프록시, TUN은 연결 방식이 서로 다릅니다. 이 글에서는 SOCKS 또는 HTTP 인바운드를 기준으로 설명해 연결 범위 문제를 라우팅 오류와 혼동하지 않도록 합니다.
2. 완성된 config.json 예시
아래는 구조를 살펴보기 위한 완성된 설정입니다. 루프백 주소의 두 포트에서 수신하고, example.org와 정확히 일치하는 도메인 요청은 차단하며 나머지 트래픽은 직접 연결합니다. example.org는 문서용 예시 도메인으로, 사용 가능한 노드를 뜻하지 않습니다. VMess 또는 VLESS 원격 아웃바운드가 없으므로 트래픽을 프록시 서버로 전달하지 않습니다.
{
"log": {
"loglevel": "warning"
},
"dns": {
"servers": [
"1.1.1.1"
]
},
"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": {}
},
{
"tag": "block",
"protocol": "blackhole",
"settings": {}
}
],
"routing": {
"domainStrategy": "AsIs",
"rules": [
{
"type": "field",
"domain": [
"full:example.org"
],
"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"
},
{
"type": "field",
"inboundTag": [
"socks-in",
"http-in"
],
"network": "tcp,udp",
"outboundTag": "direct"
}
]
}
}
표준 JSON에서는 키와 문자열에 큰따옴표를 사용해야 하며, 배열이나 객체의 마지막 항목 뒤에 쉼표를 붙이면 안 됩니다. 코드 블록에는 주석이 없어 독립적인 참고 자료로 저장할 수 있습니다. 다만 해당 코어에서 실제로 불러올 수 있는지는 코어의 설정 검사 명령으로 확인해야 합니다.
3. inbounds: 수신 주소, 포트, 인바운드 태그
inbounds는 배열입니다. 하나의 코어 프로세스가 여러 인바운드를 받을 수 있기 때문입니다. 각 인바운드의 protocol은 수신할 프로토콜을 지정하고, settings의 내부 필드는 프로토콜에 따라 달라집니다. 이 예시에서 SOCKS 인바운드의 udp: true는 SOCKS UDP 전달을 허용한다는 뜻이지, 모든 앱의 UDP 트래픽을 자동으로 가로챈다는 뜻은 아닙니다.
SOCKS 인바운드
- tag
- socks-in
- listen
- 127.0.0.1
- port
- 10808
- protocol
- socks
- auth
- noauth
앱에서 이 SOCKS 인바운드에 직접 연결해야 합니다. 루프백 주소로 수신하면 연결 범위가 이 기기로 제한됩니다.
HTTP 인바운드
- tag
- http-in
- listen
- 127.0.0.1
- port
- 10809
- protocol
- http
- settings
- 빈 객체
HTTP 프록시 요청과 CONNECT 터널에 사용하며, 로컬에 웹사이트를 여는 설정은 아닙니다.
listen과 port가 함께 수신 위치를 지정합니다. 127.0.0.1을 0.0.0.0으로 바꾸면 연결 가능한 범위가 넓어져 다른 기기에서도 해당 프록시 포트에 접근할 수 있습니다. 이 예시의 SOCKS에는 인증도 설정되어 있지 않으므로, 연결 편의를 위해 수신 범위를 함부로 넓히지 마세요. LAN에서 사용해야 한다면 인증과 방화벽도 함께 설정해야 합니다.
tag는 설정 내부에서 항목을 참조하기 위한 식별자입니다. 서버 주소도 아니고 새 포트를 자동으로 만드는 값도 아닙니다. 세 번째 라우팅 규칙의 inboundTag가 바로 여기의 두 문자열을 참조합니다. 인바운드 태그 이름을 바꾸면 해당 규칙도 함께 수정해야 하며, 대소문자와 하이픈까지 일치해야 합니다.
4. outbounds: 아웃바운드 유형과 기본 경로
outbounds는 연결이 코어를 벗어날 때의 처리 방식을 정의합니다. freedom은 이 기기에서 대상에 직접 연결하고, blackhole은 트래픽을 차단합니다. 여기서 direct와 block은 임의로 붙인 태그일 뿐입니다. 실제 동작은 태그의 이름이 아니라 protocol이 결정합니다.
| 위치 | 필드 또는 값 | 실제 역할 |
|---|---|---|
| 첫 번째 아웃바운드 | tag: direct | 라우팅 규칙에서 참조하며, 이 예시에서는 일치하는 규칙이 없을 때 기본 아웃바운드로 사용됩니다. |
| 직접 연결 프로토콜 | protocol: freedom | 대상 주소에 직접 연결합니다. |
| 두 번째 아웃바운드 | tag: block | 정확한 도메인 차단 규칙에서 참조합니다. |
| 차단 프로토콜 | protocol: blackhole | 요청을 대상 서버로 전달하지 않습니다. |
여기서 사용한 일반적인 라우팅 방식에서는 어떤 규칙에도 일치하지 않으면 첫 번째 아웃바운드를 사용합니다. 따라서 outbounds 배열의 순서를 바꾸는 것은 단순한 표시 순서 변경이 아니라 기본 트래픽 경로를 바꿀 수 있습니다. 이 예시에서는 알려진 두 인바운드에 마지막 직접 연결 규칙도 추가해 기본 동작을 쉽게 파악할 수 있도록 했습니다.
정리: 아웃바운드를 바꾸기 전에 태그 참조 확인
먼저 모든 outboundTag를 찾아 기존 아웃바운드의 tag와 일치하는지 확인하세요. 그런 다음 첫 번째 아웃바운드가 일치하지 않는 트래픽의 예상 경로인지 살펴보세요. 태그 이름을 잘못 입력해도 같은 이름의 프로토콜로 자동 대체되지는 않습니다.
원격 노드를 연결하려면 실제 매개변수가 설정되어 있고 현재 코어에서 지원하는 프로토콜의 프록시 아웃바운드를 추가한 뒤, 해당 아웃바운드를 라우팅 규칙에서 지정해야 합니다. VMess와 VLESS는 사용자 필드, 전송 계층, 보안 설정이 서로 다릅니다. protocol 문자열만 바꾼다고 변환이 끝나는 것은 아닙니다. 신뢰할 수 있는 구독을 가져온 뒤 생성된 설정을 참고해 서버, 포트, 사용자 식별자, 전송 매개변수를 하나씩 확인하세요.
5. routing: 규칙 순서와 일치 조건
routing.rules는 순서가 있는 규칙 배열입니다. 요청이 들어오면 앞에서부터 확인해 일치하는 규칙을 찾고, 지정된 아웃바운드를 사용합니다. 이후 규칙을 모두 실행하는 방식은 아닙니다. 범위가 넓은 규칙을 앞에 두면 뒤에 있는 세부 규칙이 아예 적용되지 않을 수 있습니다.
- 먼저 정확히 일치하는 도메인을 확인합니다.
full:example.org는 해당 도메인과 정확히 일치할 때만 적용되며 하위 도메인은 포함하지 않습니다. 요청에 도메인이 포함되어 있거나 코어가 도메인을 알아낼 수 있어야 이 도메인 규칙을 적용할 수 있습니다. - 다음으로 지정된 IP 대역을 확인합니다. 이 예시에는 루프백과 자주 쓰이는 사설 IPv4 대역 세 개가 있으며, 일치하면
direct로 처리됩니다. 모든 특수 주소를 포함한 목록은 아니며, 사설 IPv6나 링크 로컬 범위도 다루지 않습니다. - 마지막으로 두 로컬 인바운드를 확인합니다.
socks-in또는http-in에서 들어온 요청 중 네트워크 유형이 TCP 또는 UDP인 요청을direct로 전달합니다.
한 규칙 안에서 inboundTag 배열의 두 인바운드는 둘 중 하나만 일치해도 됩니다. 반면 같은 규칙의 서로 다른 조건 필드는 일반적으로 모두 충족해야 합니다. 따라서 세 번째 규칙은 “두 인바운드에서 들어오거나 TCP인 요청”이 아니라 “두 인바운드 중 하나에서 들어오고 네트워크 유형도 조건에 맞는 요청”을 뜻합니다.
domainStrategy: "AsIs"는 라우팅에서 IP 규칙을 적용하기 위해 도메인을 IP로 미리 해석하지 않는다는 뜻입니다. 앱이 도메인을 코어에 전달하면, 해당 도메인이 사설 IP로 해석될 수 있다는 이유만으로 두 번째 사설 주소 규칙이 적용되지는 않습니다. IP가 전달되면 대역을 바로 확인할 수 있습니다. 이 옵션은 라우팅 단계의 처리 방식만 제어하며, 연결 과정 전체에서 DNS 조회가 발생하지 않는다는 뜻은 아닙니다.
6. dns와 log: DNS 조회와 트래픽 분산을 구분하기
이 예시의 dns.servers에는 1.1.1.1이 설정되어 있습니다. 이는 코어 내장 DNS의 서버 설정이며 운영체제나 브라우저의 DNS 설정을 자동으로 대체하지 않습니다. 이 필드만 추가한다고 모든 DNS 요청이 프록시를 통과하는 것도 아닙니다. 어떤 모듈이 내장 DNS를 사용하는지, DNS 요청이 어떤 경로를 거치는지는 해당 코어와 관련 정책에 따라 확인해야 합니다.
- 라우팅 정책은 도메인 또는 IP가 규칙에 어떻게 사용되는지 결정합니다. 이 글의
AsIs는 IP 라우팅 규칙을 위해 도메인을 추가로 해석하지 않습니다. - 아웃바운드 연결에는 여전히 DNS 조회가 필요할 수 있습니다.
freedom은 최종적으로 연결을 설정해야 하며, 어떤 방식으로 주소를 해석하는지는 아웃바운드 정책과 코어 구현에도 영향을 받습니다. - 앱에서 별도로 DNS를 조회할 수도 있습니다. 앱 내 보안 DNS나 시스템 DNS가 생성한 요청이 설정에
dns가 있다는 이유만으로 반드시 코어에서 처리되는 것은 아닙니다. - 주소는 실제 네트워크 환경에 맞게 선택해야 합니다. 예시의 DNS 서버는 구조 설명용이며, 모든 네트워크에서 연결 가능하거나 사용하기 적합하다는 뜻은 아닙니다.
log.loglevel은 warning으로 설정되어 있어 경고와 오류를 우선 확인할 수 있습니다. 규칙 문제를 살펴볼 때는 잠시 info로 변경할 수 있으며, 더 자세한 로그가 필요하면 debug를 고려하세요. 로그 수준은 액세스 로그 파일 경로를 뜻하지 않으며, 모든 버전에서 같은 형식으로 출력된다는 보장도 없습니다. 로그를 공유하기 전에는 구독 주소, 사용자 식별자, 서버 주소, 접속 대상 등 민감한 정보를 가리세요.
7. 설정 수정·검증·복구 순서
클라이언트는 노드 전환, 구독 업데이트, 재시작 시 실행 설정을 다시 생성할 수 있습니다. 자동 생성된 파일을 직접 수정하면 다음 작업에서 변경 내용이 덮어써질 수 있습니다. 먼저 생성된 결과를 확인하려는 것인지 사용자 지정 설정을 사용하려는 것인지 구분한 다음, 클라이언트 인터페이스에서 수정할지 사용자 지정 설정 기능을 사용할지 결정하세요.
- 복구할 수 있도록 원본을 보관합니다. 기존 설정을 보존하고 현재 코어 유형, 설정 파일 위치, 수신 포트를 기록하세요. 구독 주소를 공개 메모에 남기지 마세요.
- 한 번에 하나의 항목만 수정합니다. 예를 들어 도메인 규칙만 추가하고 DNS, 포트, 원격 노드는 그대로 두세요. 그래야 변경의 원인을 파악하기 쉽습니다.
- 먼저 JSON 문법과 코어 설정을 검사합니다. 표준 JSON 검사는 쉼표나 따옴표 문제를 찾습니다. 코어 검사를 하면 지원되지 않는 프로토콜, 잘못된 필드 유형, 일부 참조 오류도 추가로 발견할 수 있습니다.
- 그다음 코어를 실행하고 포트 수신 상태를 확인합니다. 주소 사용 중 오류가 표시되면 10808과 10809를 클라이언트가 이미 사용하고 있는지 먼저 확인하세요. 같은 포트를 사용하는 설정을 두 개 실행하지 마세요.
- 마지막으로 실제 연결 경로를 확인합니다. 앱 요청이 실제로 로컬 인바운드에 들어오는지 확인하고, 대상 요청이 예상한 아웃바운드로 처리되는지 살펴보세요. 실패하면 직전에 정상 작동한 설정으로 복원하세요.
일반적으로 사용하는 코어 명령줄 설정 검사 방법은 다음과 같습니다. 두 방법 중 하나를 선택하고 서로 다른 코어에 연달아 적용하지 마세요. 실행 파일 이름과 호출 방식은 사용 중인 시스템에 맞춰야 합니다. 현재 버전에서 옵션을 인식하지 못한다면 해당 버전의 help 출력을 따르세요. 검사를 통과해도 설정을 받아들였다는 뜻일 뿐, 원격 서버에 연결되거나 트래픽 분산이 예상대로 작동한다는 보장은 없습니다.
v2ray test -c config.json
xray run -test -config config.json
데스크톱 시스템에 curl이 설치되어 있다면 아래 명시적 프록시 요청으로 예시를 테스트할 수 있습니다. 첫 번째 명령은 SOCKS 프록시에서 도메인을 처리하므로 full:example.org 차단 규칙을 확인하는 데 적합합니다. 두 번째 명령은 HTTP 인바운드를 통해 다른 문서용 예시 도메인에 접속하며 직접 연결되어야 합니다. 두 명령 모두 코어가 정상적으로 실행 중이고 포트가 이 예시와 일치할 때 실행하세요.
curl --proxy socks5h://127.0.0.1:10808 --connect-timeout 10 https://example.org/
curl --proxy http://127.0.0.1:10809 --connect-timeout 10 https://example.com/
첫 번째 요청은 연결 실패가 예상되지만, 실패 메시지만으로는 차단 규칙, 인바운드 미실행, 네트워크 장애를 구분할 수 없습니다. 두 번째 요청의 성공 여부도 실제 네트워크 환경에 따라 다릅니다. 명령 결과와 코어 로그를 함께 확인해야 하며, 웹페이지가 열리지 않았다는 이유만으로 특정 규칙이 적용되었다고 판단하면 안 됩니다.
8. 설정을 읽을 때 자주 묻는 질문
구독을 가져온 뒤 구독 내용을 config.json으로 바로 저장하면 안 되나요?
구독은 클라이언트가 노드를 식별하고 업데이트하는 데 사용하는 데이터이며, 코어에서 바로 불러올 수 있는 완성된 설정 파일과는 다를 수 있습니다. 공유 링크나 인코딩된 목록이 포함될 수 있고, 로컬 수신 인바운드를 정의하지 않는 경우도 많습니다. 먼저 클라이언트에서 가져온 뒤 생성된 실행 설정을 확인하세요. 파일 확장자만 바꾸면 되는 것은 아닙니다.
SOCKS 포트와 HTTP 포트를 서로 바꿔도 되나요?
포트 번호 자체에는 프로토콜이 지정되어 있지 않아 설정에서 바꿀 수 있지만, 앱에서도 같은 포트로 수정해야 합니다. 이 글의 10808을 HTTP 프록시 주소로 입력해도 해당 포트에 연결된다는 이유만으로 HTTP 인바운드가 되지는 않습니다. 먼저 protocol을 확인한 다음 포트와 앱에서 사용하는 프록시 유형을 확인하세요.
차단 규칙이 있는데도 요청이 직접 연결되는 이유는 무엇인가요?
네 가지를 순서대로 확인하세요. 앱이 실제로 프록시 인바운드를 사용하는지, 코어에 도메인과 IP 중 무엇이 전달됐는지, 앞선 규칙이 먼저 적용됐는지, 수정한 설정이 실제로 실행 중인지 살펴보세요. 정확한 도메인 규칙은 하위 도메인에 적용되지 않는다는 점도 유의해야 합니다. 처음부터 노드를 바꿔 규칙 문제를 덮으려 하지 마세요.
이 예시를 v2rayNG의 실행 설정으로 바로 바꿔도 되나요?
바로 교체하면 안 됩니다. Android 클라이언트는 VPN 연결, DNS, 앱별 트래픽 분산 등의 설정에 맞춰 실행 구성을 생성해야 합니다. 이 글에서는 로컬 SOCKS, HTTP, 라우팅의 기본 구조만 설명합니다. 실제 설정은 클라이언트의 해당 기능을 사용해 조정하고 복구 가능한 설정을 보관하세요. 완전한 프록시 기능을 사용하려면 유효한 원격 아웃바운드도 필요합니다.