이전 글들에서 퍼피티어와 일렉트론을 이용한 프로그램을 개발을 통해 사용자가 입력과 실행, 그리고 로그인만 하면 페이지 이동, API 호출, OCR을 통해 바로 좌석 선택 후 결제 페이지까지 넘어가는 과정까지 마무리했습니다. 이번 글에서는 모든 예매 사이트에서 대기열을 관리하기 위한 넷퍼넬과 많은 분들이 물어보신 예매 버튼 활성화에 대해 정리해보려고 합니다. 이전 글에서 넷퍼넬에 대해서 간단하게 설명한 적이 있습니다. 이번 글에서는 넷퍼넬을 우회하는게 아닌, 넷퍼넬에 대해 이해해보고, 실제로 어떤 기준으로 검증을 하는지 알아보도록 합시다.
[NetFunnel 우회] 직링 구하기
지난 글에서는 직링에 대한 간단한 소개와, 페이지 내에서 개발자 도구(F12) 혹은 크롬 익스텐션을 이용해서 몇몇 과정을 건너뛰고, 바로 좌석 선택 화면으로 이동하는 방법에 대해 소개해드렸습
gnaaak.tistory.com
넷퍼넬(NetFunnel)이란?
넷퍼넬은 웹 사이트에 동시 접속자 수를 제어해 서버 과부하를 방지하고, 사용자에게 안정적인 서비스를 제공하는 트래픽 관리 솔루션입니다. 흔히 '대기열 시스템', 또는 '접속 제한 솔루션' 으로 불리며, 대규모 트래픽이 몰리는 이벤트, 예매, 쇼핑몰 등의 환경에서 주로 사용됩니다. 가장 중요한 기능으로는 일정 수 이상의 사용자가 동시에 접속하려고 하면 대기열에 넣고 순차적으로 서비스에 접근하도록 조절하는 접속 제어(Queueing System)이라고 할 수 있겠네요.
동작 원리로는 사용자 접속 요청이 발생하면, 넷퍼넬 JS가 로딩되어 서버와 통신하여 입장 가능 여부를 판단해줍니다. 입장 가능 시 실제 웹서버에 요청을 전달하여 서비스를 사용자가 이용할 수 있게 됩니다. 그렇다면 실제로 넷퍼넬 요청을 보내보고, 넘어오는 값들을 확인해보도록 합시다.
/ts.wseq?opcode=5101
&nfid=0
&prefix=NetFunnel.gRtype=5101;
&ttl=1
&sid=service_1
&aid=*****
&js=yes
&user_data=*****
&1750855531873
처음 넷퍼넬 대기열에 진입할 때 url입니다. 다음은 응답입니다.
NetFunnel.gRtype=5101;
NetFunnel.gControl.result=
'5002:201:key=5234CCE2BD0F694DE09572B3952247B7F2A3D5BCFA8A11F2BE7175624CE182A07AA1634D7ED21BB380034DDDF40482CD4608820F03C134B42B150E9D63771CB281D3562C0520B26BBEAF69647B3BBCFFA54C30C4D3C9936B8C56E4B1BF3C91694809C295DA70C848E9F882E594AC748E434A2C302C352C312C363034362C30
&nwait=6046
&nnext=1
&tps=1.399680
&ttl=1
&ip=***
&port=443';
NetFunnel.gControl._showResult();
응답에서 눈여겨봐야 하는 곳은 5002:201:key= 부분과 현재 남은 대기열인 nwait, 그리고 내 뒤에 대기자 수인 nnext로 보입니다. 초기에 응답을 받고 그 키를 사용해서 대기열을 기다리는 형식이니 이 이후 요청들에서 규칙을 찾으면 될 것 같습니다. 이전 요청의 응답이 이번 요청의 키 값이 되는 구조로 작동하므로 보낸 요청들에서 공통점을 찾아봅시다. 아래는 10번의 요청 url의 key 부분입니다.
ts.wseq?opcode=5002&key=5234CCE2BD0F694DE09572B3952247B7F2A3D5BCFA8A11F2BE7175624CE182A07AA1634D7ED21BB380034DDDF40482CD4608820F03C134B42B150E9D63771CB281D3562C0520B26BBEAF69647B3BBCFFA54C30C4D3C9936B8C56E4B1BF3C91694809C295DA70C848E9F882E594AC748E434A2C302C352C312C363034362C30
ts.wseq?opcode=5002&key=5234CCE2BD0F694DE09572B3952247B7F2A3D5BCFA8A11F2BE7175624CE182A08E0E341B5D08F0789319C3C0A5D392B84608820F03C134B42B150E9D63771CB281D3562C0520B26BBEAF69647B3BBCFFA54C30C4D3C9936B8C56E4B1BF3C91694809C295DA70C848E9F882E594AC748E434A2C302C342C312C363034312C30
ts.wseq?opcode=5002&key=5234CCE2BD0F694DE09572B3952247B7F2A3D5BCFA8A11F2BE7175624CE182A00A6F8C5A6051B47800F5C31C7B3F31164608820F03C134B42B150E9D63771CB281D3562C0520B26BBEAF69647B3BBCFFA54C30C4D3C9936B8C56E4B1BF3C91694809C295DA70C848E9F882E594AC748E434A2C302C332C312C363034312C30
ts.wseq?opcode=5002&key=5234CCE2BD0F694DE09572B3952247B7F2A3D5BCFA8A11F2BE7175624CE182A007C1630895C3968EA0D80BEABD3ECE1E4608820F03C134B42B150E9D63771CB281D3562C0520B26BBEAF69647B3BBCFFA54C30C4D3C9936B8C56E4B1BF3C91694809C295DA70C848E9F882E594AC748E434A2C302C322C312C363033342C30
ts.wseq?opcode=5002&key=5234CCE2BD0F694DE09572B3952247B7F2A3D5BCFA8A11F2BE7175624CE182A042570E69CC1B48B91AC54B7B4ED228354608820F03C134B42B150E9D63771CB281D3562C0520B26BBEAF69647B3BBCFFA54C30C4D3C9936B8C56E4B1BF3C91694809C295DA70C848E9F882E594AC748E434A2C302C312C312C363033342C30
ts.wseq?opcode=5002&key=5234CCE2BD0F694DE09572B3952247B7F2A3D5BCFA8A11F2BE7175624CE182A04407ABD1612081423892D9ED2718128D4608820F03C134B42B150E9D63771CB281D3562C0520B26BBEAF69647B3BBCFFA54C30C4D3C9936B8C56E4B1BF3C91694809C295DA70C848E9F882E594AC748E434A2C302C302C312C363033342C30
ts.wseq?opcode=5002&key=5234CCE2BD0F694DE09572B3952247B7F2A3D5BCFA8A11F2BE7175624CE182A02E688190514094958F667B41A9D239004608820F03C134B42B150E9D63771CB281D3562C0520B26BBEAF69647B3BBCFFA54C30C4D3C9936B8C56E4B1BF3C91694809C295DA70C848E9F882E594AC748E8895E09E7273E13F286A5220B732687B
ts.wseq?opcode=5002&key=5234CCE2BD0F694DE09572B3952247B7F2A3D5BCFA8A11F2BE7175624CE182A0149E470C7607393EC1D82D39633281224608820F03C134B42B150E9D63771CB281D3562C0520B26BBEAF69647B3BBCFFA54C30C4D3C9936B8C56E4B1BF3C91694809C295DA70C848E9F882E594AC748E434A2C302C392C312C363033302C30
ts.wseq?opcode=5002&key=5234CCE2BD0F694DE09572B3952247B7F2A3D5BCFA8A11F2BE7175624CE182A04856C3DE457CC833A51BA79BBD94862B4608820F03C134B42B150E9D63771CB281D3562C0520B26BBEAF69647B3BBCFFA54C30C4D3C9936B8C56E4B1BF3C91694809C295DA70C848E9F882E594AC748E434A2C302C382C312C363033302C30
ts.wseq?opcode=5002&key=5234CCE2BD0F694DE09572B3952247B7F2A3D5BCFA8A11F2BE7175624CE182A0FA66A278EEA7F1FECC60E6913B3CD4514608820F03C134B42B150E9D63771CB281D3562C0520B26BBEAF69647B3BBCFFA54C30C4D3C9936B8C56E4B1BF3C91694809C295DA70C848E9F882E594AC748E434A2C302C372C312C363032332C30
받아온 키들은 128바이트(256자)의 고정된 길이를 가지고 있습니다. 그 중 앞 64자리는 5234CC...로 고정된 것을 볼 수 있습니다. 이후 32자는 고유값, 128자는 고정 값, 그리고 다시 32자는 고유값으로 유추해 볼 수 있습니다.
마지막 32자 434A2C302C352C312C363034362C30를 ASCII로 변환하는 과정을 거치면 CJ,0,5,1,6046,0라는 값이 나옵니다. 여기서 6046은 nwait 값입니다. 그 다음 요청들은 CJ,0,4,1,6041,0, CJ,0,3,1,6041,0, CJ,0,2,1,6034,0와 같은 값들이 나옵니다. 그 이후 값들을 변환하면 아래와 같이 CJ + 내림차순 + 1 + nwait + 0의 형태를 보여줍니다.
434A2C302C312C312C363033342C30 → CJ,0,1,1,6034,0
434A2C302C302C312C363033342C30 → CJ,0,0,1,6034,0
434A2C302C392C312C363033302C30 → CJ,0,9,1,6030,0
434A2C302C382C312C363033302C30 → CJ,0,8,1,6030,0
434A2C302C372C312C363032332C30 → CJ,0,7,1,6023,0
434A2C302C362C312C363031322C30 → CJ,0,6,1,6012,0
434A2C302C352C312C353939352C30 → CJ,0,5,1,5995,0
434A2C302C342C312C353938342C30 → CJ,0,4,1,5984,0
434A2C302C332C312C353937372C30 → CJ,0,3,1,5977,0
434A2C302C322C312C353935392C30 → CJ,0,2,1,5959,0
434A2C302C312C312C353934312C30 → CJ,0,1,1,5941,0
434A2C302C302C312C353934312C30 → CJ,0,0,1,5941,0
중간 32자는 아마 서버에서 사용하는 시크릿 키 + 특정 조합으로 생성해서 보내주는 것 같습니다. 처음에 응답을 받고 그걸 바탕으로 서버에서 생성하는 키 값을 알 수 있다면, 바로 대기열을 앞쪽으로 당기는 것도 가능해 보입니다.
다음은 예매 버튼 활성화입니다. 사실 이 부분은 의미가 크게 없다고 생각합니다. 서버가 열려있지 않은 상태에서의 요청은 무의미하고, 서버가 열리는 시간에 API로 호출을 보내는 코드를 짠 입장에서 호출을 100ms 단위로만 보낸다고 해도 0.1초단위로 보내는데, 사람 클릭이 더 빠를 수 있습니까..? 우선 구현할 수 있는 간단한 방법입니다.

예매하기 버튼의 DOM 구조입니다. data-prodid를 다른 콘서트의 prodid로 바꾸면 실제로 버튼은 클릭되면서 해당 콘서트로 요청이 보내지게 됩니다.

어썸스테이지에서 예매하기 버튼 id를 바꿔 Festival #5를 호출한 모습입니다. 개발자도구에 접근 가능하고, API 호출 방식을 모르는 상태에서는 유의미할 수 있을 것 같긴 하지만 개인적으로는 DOM 조작(클릭이나 이동 등)까지는 유의미하지만, DOM 변경은 무의미하다고 생각합니다. 대부분의 경우 컴퓨터가 사람보다 처리 속도가 빠르니까요.
이렇게 넷퍼넬 분석과 예매 버튼 활성화에 대해 얘기해봤습니다. 넷퍼넬 키를 실제로 분석하기는 힘들겠지만, 분석만 가능하다면 자동화로 대기열 새치기 같은 것도 가능하겠네요.
좋을 개발 도구나 자동화 아이디어가 있다면 댓글로 추천 부탁드립니다! 이제 어떤 걸 개발해볼 지 고민 중이라 여러분의 아이디어가 큰 도움이 될 것 같아요.
언제나처럼 — 시작은 삽질이지만, 끝은 지식입니다.
'자유로운 개발일지 > 직링' 카테고리의 다른 글
| [직링] 스포츠 티켓 (21) | 2025.08.25 |
|---|---|
| [직링] 취소표 매크로 (13) | 2025.08.17 |
| [직링] 좌석 선택 (6) | 2025.07.08 |
| [직링] 기능 개발 (4) | 2025.07.03 |
| [직링 이해하기] HTTP 요청 방식 (19) | 2025.07.02 |
