클라이언트가 생성한 설정을 읽거나 포트 및 트래픽 분기 문제를 해결하려는 사용자를 위한 글입니다. 로컬 인바운드, 직접 연결 및 차단 출구만 포함한 전체 설정으로 각 항목의 관계를 살펴본 다음 원격 프록시, 규칙 순서, DNS의 역할을 설명합니다. 예제는 설정 구조를 확인하기 위한 것으로 연결 가능한 원격 프록시 노드는 포함하지 않습니다. 글을 읽고 나면 요청이 어디로 들어와 어떤 규칙에 일치하고 최종적으로 어떤 출구를 사용하는지 파악할 수 있습니다.
1. config.json 최상위 구조와 요청 흐름
config.json은 흔히 쓰이는 설정 파일 이름이지, 고정된 이름은 아닙니다. 실제로 어떤 파일을 불러올지는 커널 실행 인수가 결정합니다. 이 글에서는 V2Ray의 기존 JSON 설정 구조를 사용합니다. V2Ray와 Xray는 공통된 항목이 있지만 확장 기능과 버전별 지원 범위가 완전히 같지는 않으므로, 한 커널의 새 항목을 다른 커널에 그대로 복사하면 안 됩니다.
inbounds는 앱 트래픽을 받아들이고, outbounds는 연결 방식을 지정하며, routing은 사용할 출구를 결정합니다. log는 실행 정보를 기록하고 dns는 내장 DNS 리졸버를 설정합니다. JSON 객체에서 최상위 키의 순서는 보통 실행 결과에 영향을 주지 않지만, 라우팅 규칙 배열의 순서는 결과를 바꿀 수 있습니다.
| 최상위 항목 | 데이터 형식 | 먼저 확인할 사항 |
|---|---|---|
log | 객체 | 시작 오류와 접속 기록을 확인할 수 있는가 |
inbounds | 배열 | 앱 연결의 프로토콜·주소·포트가 일치하는가 |
outbounds | 배열 | 아웃바운드 프로토콜과 매개변수가 모두 설정되어 있는가 |
routing | 객체 | 규칙 순서와 출구 태그가 일치하는가 |
dns | 객체 | 어떤 단계에서 내장 DNS 리졸버를 실제로 사용하는가 |
아래 설정은 독립 실행 커널의 구조를 테스트하는 데 사용할 수 있습니다. 로컬 포트 두 개를 열고 example.org와 하위 도메인은 차단하며, 나머지 요청은 기본적으로 직접 연결합니다. 원격 프록시 서버로 트래픽을 보내지 않으며 시스템 프록시도 자동으로 켜지지 않습니다.
{
"log": {
"loglevel": "warning"
},
"inbounds": [
{
"tag": "socks-in",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": false
}
},
{
"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": ["domain:example.org"],
"outboundTag": "block"
},
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "direct"
}
]
},
"dns": {
"servers": ["1.1.1.1"]
}
}
2. log: 설정 오류를 찾기 쉽게 만들기
loglevel에 자주 사용하는 값은 debug, info, warning, error, none입니다. 예제에서는 일상적인 로그 출력을 줄이기 위해 warning을 사용합니다. 시작 또는 연결 문제를 진단할 때는 잠시 info로 바꾸고, 더 자세한 내부 기록이 필요하면 debug를 사용하세요.
{
"log": {
"access": "access.log",
"error": "error.log",
"loglevel": "info"
}
}
이 코드는 최상위 설정 조각입니다. 전체 설정의 log 항목을 교체해야 하며, 같은 키를 중복해서 추가하면 안 됩니다. 상대 경로로 지정한 로그 파일은 설정 파일이 아니라 프로세스의 작업 디렉터리를 기준으로 저장됩니다. 클라이언트가 커널을 실행하면 작업 디렉터리, 파일 쓰기 권한, 로그 표시 방식이 달라질 수 있으므로 우선 클라이언트의 실행 로그를 확인하세요.
- 시작 직후 실패함: JSON 파싱 오류, 알 수 없는 항목, 포트 바인딩 실패부터 확인하세요.
- 앱 요청 기록이 없음: 앱이 올바른 인바운드에 연결되어 있는지 먼저 확인하세요. 원격 프로토콜 매개변수부터 바꾸지 마세요.
- 요청이 들어온 뒤 연결 실패: 아웃바운드, 대상 주소, 도메인 조회, 네트워크 연결 상태를 차례로 확인하세요.
- 로그를 공유하기 전에 서버 주소, 사용자 식별자, 구독 주소, 접속 도메인 등 민감한 정보를 삭제하세요.
3. inbounds: 수신 주소, 포트, 프록시 프로토콜
inbounds는 배열이므로 여러 진입점을 동시에 제공할 수 있습니다. 각 항목의 protocol은 앱에서 들어오는 데이터를 해석하는 방식을 결정합니다. SOCKS와 HTTP 프록시는 서로 다른 프로토콜입니다. 둘 다 로컬에서 수신하더라도 앱에서 잘못된 유형을 선택하면 연결에 실패할 수 있습니다.
이 포트는 이 글에서 지정한 예시 값이며 모든 버전의 클라이언트 기본값을 의미하지 않습니다. 앱에서 HTTP 프록시를 설정할 때는 주소 127.0.0.1, 포트 10809를 입력하세요. SOCKS5 프록시는 127.0.0.1:10808을 입력하면 됩니다.
SOCKS 로컬 진입점
- 태그
socks-in- 수신 주소
127.0.0.1:10808- 프로토콜
socks- 인증
noauth- UDP
false
이 예제는 로컬 TCP 요청만 다루며 SOCKS UDP 전달은 사용하지 않습니다.
HTTP 로컬 진입점
- 태그
http-in- 수신 주소
127.0.0.1:10809- 프로토콜
http- 설정
{}
HTTP 프록시를 지원하는 앱에서 사용하며, HTTPS 요청은 보통 CONNECT로 터널을 만듭니다.
listen을 루프백 주소로 설정하면 로컬 기기에서만 연결할 수 있습니다. 0.0.0.0으로 바꾸면 모든 IPv4 인터페이스에서 수신하므로 같은 네트워크의 다른 기기도 진입점에 접속할 수 있습니다. 공유가 필요하다면 수신 주소만 바꾸지 말고 인증, 방화벽, 접근 범위도 함께 설계하세요.
tag는 진입점의 식별자이며 라우팅 규칙에서inboundTag로 참조할 수 있습니다.- 포트를 이미 사용 중이면 시작에 실패합니다. 테스트 커널을 실행하기 전에 같은 포트를 사용하는 클라이언트를 종료하세요.
- 시스템 프록시는 앱이 프록시에 연결하는 한 가지 방법일 뿐입니다. 시스템 프록시를 무시하는 앱은 대상에 직접 연결할 수 있습니다.
- 도메인 스니핑은 별도로 설정해야 합니다. 이 글에서는 스니핑을 사용하지 않으므로 커널이 대상 IP에서 항상 도메인을 알아낼 수 있다고 가정하면 안 됩니다.
4. outbounds: 아웃바운드 프로토콜과 태그의 관계
outbounds는 커널에서 트래픽을 내보내는 방식을 정의합니다. 예제의 freedom은 직접 연결 출구이고 blackhole은 차단 출구입니다. direct와 block은 임의로 붙인 태그일 뿐입니다. 태그 자체가 출구 기능을 정하지는 않으며, 실제 동작은 protocol과 관련 설정에 따라 결정됩니다.
| 출구 태그 | 프로토콜 | 이 글에서의 동작 |
|---|---|---|
direct | freedom | 대상에 직접 연결하며 규칙에 일치하지 않은 요청의 기본 출구로 사용 |
block | blackhole | 일치한 요청을 차단하며 원격 프록시에 연결하지 않음 |
proxy | 실제 노드 설정에 따라 결정 | 이 글에서는 정의하지 않음. 완전한 프록시 출구를 추가한 뒤에만 참조할 수 있음 |
이와 같은 설정에서는 라우팅 규칙에 일치하지 않은 요청이 outbounds 배열의 첫 번째 출구를 기본으로 사용합니다. 이 글에서는 direct를 첫 번째에 두었으므로 기본적으로 직접 연결합니다. 첫 번째 항목을 실제 프록시 출구로 바꾸면 규칙에 일치하지 않은 트래픽의 경로도 달라집니다.
VMess 또는 VLESS 아웃바운드에는 실제 서버 주소와 포트, 사용자 식별자, 서버와 일치하는 전송 및 보안 매개변수가 필요합니다. WebSocket 경로나 TLS 서버 이름 등의 값은 프로토콜 이름만으로 추측할 수 없습니다. Xray 확장 기능을 사용하는 설정은 커널의 지원 범위도 확인해야 합니다. 모든 VLESS 설정이 일반적인 V2Ray 설정으로 호환되는 것은 아닙니다.
요약: 태그를 먼저 확인한 뒤 노드 매개변수 조정
각 규칙의 outboundTag와 아웃바운드의 tag를 한 글자씩 대조하고, 첫 번째 출구가 기본 트래픽 정책에 맞는지 확인하세요. 이 두 가지가 정확한지 확인한 뒤 서버와 전송 설정을 점검하면 됩니다.
5. routing: 규칙 순서와 outboundTag
routing.rules는 배열에 적힌 순서대로 검사하며, 규칙에 일치하면 지정된 출구를 사용합니다. 범위가 지나치게 넓은 규칙을 앞에 두면 뒤에 있는 세부 규칙이 적용되지 않을 수 있습니다. 한 규칙 안의 서로 다른 조건 항목은 보통 모두 만족해야 하며, 같은 항목의 배열에 있는 후보는 보통 하나만 일치해도 됩니다.
{
"type": "field",
"inboundTag": ["http-in"],
"domain": ["domain:example.org"],
"outboundTag": "block"
}
위 설정은 조건을 조합하는 방법을 보여줍니다. 요청이 http-in에서 들어오고 대상 도메인이 example.org 또는 하위 도메인에 해당할 때만 block으로 처리됩니다. 도메인 하나만 정확히 지정하려면 접미사 매칭 대신 full:example.org를 사용하세요.
- 먼저 이 글의
direct,block처럼 실제로 설정한 출구 태그를 확인하세요. - 우선 적용할 세부 규칙을 앞에 두고 범위가 넓은 규칙은 뒤에 배치하세요.
- 각
outboundTag를 대조하세요. 철자, 대소문자, 하이픈이 출구 태그와 정확히 일치해야 합니다. - 인바운드가 도메인을 받았는지 IP 주소를 받았는지 확인하세요. 대상 정보에 따라 일치하는 규칙이 달라질 수 있습니다.
- 규칙에 일치하지 않은 트래픽은 첫 번째 출구로 간다는 점을 명확히 하세요. ‘일치하는 규칙 없음’이 자동 차단을 뜻하지는 않습니다.
| domainStrategy | 라우팅 처리 방식 | 사용 시 참고 사항 |
|---|---|---|
AsIs | 현재 대상 정보를 사용하며 라우팅 매칭을 위해 도메인을 능동적으로 조회하지 않음 | 대상이 원래 IP 주소라면 IP 규칙과 계속 매칭 가능 |
IPIfNonMatch | 도메인이 규칙에 일치하지 않으면 조회를 시도한 뒤 IP 규칙과 매칭 | 조회 단계가 추가될 수 있으며 결과는 DNS 응답 주소에 따라 달라짐 |
IPOnDemand | IP 판정이 필요한 규칙을 만나면 조회 실행 | 규칙 순서를 고려해 조회 시점을 판단해야 함 |
이 글에서는 AsIs를 사용합니다. 따라서 앱이 사설 IP를 전달하면 geoip:private에 일치할 수 있지만, 사설 IP로 최종 조회되는 도메인을 앱이 전달하는 경우에는 이 IP 규칙이 적용되지 않을 수 있습니다. 라우팅 단계에서 능동적으로 조회하지 않는다고 해서 실제 연결 과정에서도 DNS 조회가 전혀 필요 없는 것은 아닙니다.
6. dns: 내장 DNS 리졸버와 시스템 DNS는 다릅니다
dns.servers는 커널의 내장 DNS 리졸버가 사용할 서버를 지정합니다. 이 글의 1.1.1.1은 일반 DNS 주소의 예시일 뿐 암호화 DNS가 활성화된다는 의미가 아니며, 현재 네트워크에서 연결 가능하다는 보장도 없습니다. 실제 네트워크 환경에 맞는 조회 방식을 선택하고 조회 요청이 대상 서버에 도달하는지 확인하세요.
- 운영체제, 브라우저, 커널이 각각 DNS 조회를 수행할 수 있습니다.
dns를 설정해도 시스템의 모든 DNS 요청이 자동으로 바뀌지는 않습니다. - 라우팅에서 DNS 조회가 필요한 정책을 사용하면 내장 DNS가 도메인과 IP의 매칭 과정에 참여할 수 있습니다.
freedom아웃바운드에는 자체domainStrategy가 있습니다. 기본 동작은 내장 DNS를 능동적으로 호출하는 정책과 다릅니다.- 도메인을 원격 프록시에서 처리할 때 DNS 조회 위치는 아웃바운드 프로토콜과 대상 표현 방식에 따라서도 달라집니다.
예를 들어 직접 연결 출구가 내장 DNS를 통해 IPv4 주소를 능동적으로 조회하도록 하려면, 커널 지원 여부를 확인한 뒤 해당 출구 설정을 아래와 같이 바꾸면 됩니다. 이는 아웃바운드 설정이지 라우팅 설정이 아닙니다. 두 곳에서 모두 domainStrategy라는 이름을 사용하지만 적용 범위는 다릅니다.
{
"tag": "direct",
"protocol": "freedom",
"settings": {
"domainStrategy": "UseIPv4"
}
}
7. 설정 검증과 클라이언트 사용 시 주의사항
먼저 문법을 확인하고, 다음으로 포트를 열어 요청 경로를 검증하세요. 일반 JSON에서는 주석과 마지막 항목 뒤의 쉼표를 사용할 수 없습니다. 코드를 복사할 때 해당 객체를 교체하고 같은 이름의 최상위 키를 중복으로 남기지 마세요. 일부 로더가 확장 문법을 허용하더라도 다른 커널이나 클라이언트에서 처리할 수 있다는 뜻은 아닙니다.
- 기존 설정을 백업하고 실제로 불러오는 파일을 수정하는지 확인하세요.
- 현재 커널의 설정 테스트 명령을 사용해 항목 오류와 리소스 파일 오류를 먼저 해결하세요.
10808및10809를 사용하는 프로세스를 종료한 뒤 테스트 커널을 실행하세요.- 시스템 프록시의 활성화 여부에 기대지 말고 프록시 포트를 명시해 요청을 보내세요.
- 차단 대상 도메인과 일반 대상의 결과를 확인한 뒤 실제 프록시 출구와 복잡한 규칙을 하나씩 추가하세요.
V2Ray 5 시리즈에서는 일반적으로 다음 명령으로 기존 JSON 설정을 테스트합니다. 배포 패키지에 따라 실행 파일 위치가 다를 수 있으므로 먼저 v2ray help를 실행해 하위 명령을 확인하세요. 구버전의 -test -config 인수 형식을 새 하위 명령과 함께 사용하지 마세요.
v2ray test -c config.json
v2ray run -c config.json
커널이 정상적으로 시작되면 프록시를 지원하는 요청 도구로 경로를 확인할 수 있습니다. 아래 두 명령은 모두 이 글에서 설정한 차단 도메인에 요청하므로, 요청 실패 또는 연결 종료가 예상됩니다. 규칙이 적용되는지 확인하기 위한 것으로 연결 성공 여부를 테스트하는 명령은 아닙니다. 두 번째 명령의 socks5h는 도메인을 프록시 측에 전달하므로 도메인 매칭 조건을 유지하는 데 도움이 됩니다.
curl --max-time 10 --proxy http://127.0.0.1:10809 http://example.org/
curl --max-time 10 --proxy socks5h://127.0.0.1:10808 http://example.org/
v2rayN은 데스크톱 클라이언트로, 노드·라우팅·설정에 따라 커널 실행 설정을 생성합니다. 생성된 파일은 노드를 바꾸거나 클라이언트를 다시 시작할 때 덮어쓰일 수 있습니다. v2rayNG는 Xray 커널을, v2flyNG는 V2Fly 커널을 사용합니다. 실행 설정을 살펴보면 비슷한 항목을 이해하는 데 도움이 되지만, 사용자 지정 JSON을 가져올 수 있는지와 인터페이스 설정과 함께 사용할 수 있는지는 해당 클라이언트 버전을 기준으로 확인하세요.
config.json을 수정했는데 클라이언트 트래픽이 바뀌지 않는 이유는 무엇인가요?
현재 실행 중인 프로세스가 불러온 파일을 수정했는지, 과거에 내보낸 파일을 수정한 것은 아닌지 먼저 확인하세요. 이어서 클라이언트가 설정을 다시 생성했는지, 커널이 재시작되었는지, 앱의 요청이 해당 인바운드를 거치는지 확인하세요. 구독 노드 매개변수, 클라이언트 라우팅 프리셋, 전체 커널 설정은 서로 다른 계층이므로 한 계층을 수정해도 다른 계층에 반드시 반영되지는 않습니다.
설정 테스트를 통과하면 프록시 노드가 반드시 작동하나요?
설정 테스트는 주로 설정을 불러올 수 있는지 확인할 뿐, 원격 서버의 작동 여부를 증명하지 않습니다. 사용자 식별자, 전송 경로, TLS 매개변수가 서버와 일치하는지도 확인할 수 없습니다. 이 글의 예제에는 원격 프록시 출구가 없습니다. 실제 노드를 검증하려면 완전한 아웃바운드를 추가하고 실제 요청으로 연결 결과를 확인해야 합니다.
도메인 트래픽 분기만 작동하지 않는다면 무엇부터 확인해야 하나요?
먼저 앱이 인바운드에 도메인을 전달하는지 이미 조회된 IP를 전달하는지 확인하세요. 그런 다음 domain:과 full:의 매칭 범위를 확인하고, 마지막으로 규칙 순서와 domainStrategy를 점검하세요. 대상 정보를 확인하기 전에 DNS, 스니핑, 모든 출구 매개변수를 한꺼번에 바꾸지 마세요.