일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |
- 자동화
- WebRTC
- uvicorn
- kotlin
- fiddler
- EC2
- ubuntu
- WSL
- linux
- 일렉트론
- 자동화 도구
- 예매
- 개발자 도구
- 프록시
- 티켓
- puppeteer
- FastAPI
- 콘서트
- 퍼피티어
- 개발자 도구 우회
- AWS
- GPT
- nginx
- 직링
- App
- 티켓링크
- Django
- selenium
- 피들러
- realtime
- Today
- Total
개발 삽질 일지
[직링] 좌석 선택 본문
이전 글에서는 스케일 업(Scale Up) 과정의 일환으로, 지정 PC 인증 기능에 대해 소개드린 바 있습니다. 해당 기능을 통해 이제는 프로그램을 단순히 설치만 한다고 누구나 사용할 수 있는 것이 아니라, 사전에 지정된 사용자만 접근 가능하도록 제어할 수 있게 되었습니다. 이를 통해 보안성과 운영 안정성이 한층 강화되었죠. 이번 글에서는 또 다른 스케일 업의 방향으로, 좌석 선택 기능에 대해 말씀드리려 합니다. 또한, 이번 글에서는 코드 구현보다는 개념과 작동 원리에 집중하니 가볍게 읽어주시길 바랍니다.
우선 어떤 방식으로 좌석을 선택할 지 고민하기 위해 인터파크 아무 콘서트나 들어가 좌석배치와 요소를 찍어봅시다. 좌석은 아래와 같습니다.
개발자 도구로 확인한 요소들의 값은 아래와 같습니다. SeatN이 비어있는 자리, SeatR이 이미 예매된 자리라는 것을 확인하실 수 있습니다. 그렇다면 SeatN 중 특정 조건을 만족하는 좌석을 클릭하고, 다음 단계로 넘어가면 될 것 같습니다.
다른 사이트의 경우에는 다른 방식으로 좌석들을 구분할 가능성이 큽니다. 우선 살펴본 봐로는 SelectSeat안에 변수로 들어가는 값들 중 5번째 값이 좌석 번호인 것 같습니다. 그 외에는 딱히 구분지을만한게 없어보입니다. 운이 없으면 2연석으로 진행한다고 했을 때, 열 맨 우측과 다음 열 맨 좌측이 걸릴 수도 있겠네요. x를 눌러 조의를 표합니다.
다음은 코드입니다.
3000ms, 3초까지 SeatN을 찾는 함수입니다. 만약에 3초가 지났을 때 까지 없다면(남은 좌석이 없다는 뜻이겠죠?) 위에 While문을 돌러 밑에 코드들을 무시해버립니다.
try {
await frmSeatDetail.waitForSelector(".SeatN", {
timeout: 3000,
});
} catch (e) {
await new Promise((res) => setTimeout(res, 300));
continue;
}
1단계 : 좌석 정보
const seatElements = await frmSeatDetail.$$(".SeatN");
const parsedSeats = await Promise.all(
seatElements.map(async (el, idx) => {
const onclick = await el.evaluate((el) => el.getAttribute("onclick"));
let seatNum = null;
let zoneCode = null;
if (onclick) {
const parts = onclick.split(",");
if (parts.length >= 6) {
seatNum = parseInt(parts[4].replace(/['")]/g, ""), 10);
zoneCode = parts[5].replace(/['")]/g, "");
}
}
return {
seatNum,
zoneCode,
handle: el,
raw: onclick,
};
}),
);
클래스가 SeatN인 좌석들을 모두 모아 onclick의 값을 받아옵니다. 위 요소로 예시를 들면 "Select(this, '3', '2층', '34구역', '99', '234', 'S석')" 이 되겠네요. 이 정보를 (,)를 기준으로 나눠줍시다. "Select(this / '3' / '2층'... /'S석')" 등으로 나누어집니다. 5번째 값인 99만 있으면 연속성을 판단할 수 있을 것 같습니다. 가져와줍시다.
2단계 : 연석 판별
for (let i = 0; i <= parsedSeats.length - seatCountNo; i++) {
const current = parsedSeats.slice(i, i + seatCountNo);
const sameZone = current.every((s) => s.zoneCode === current[0].zoneCode);
if (!sameZone) continue;
const ascending = current.every((s, j) =>
j === 0 ? true : Number(s.seatNum) === Number(current[j - 1].seatNum) + 1
);
if (ascending) {
const toClick = current.map((s) => s.element);
let clickSuccess = true;
for (const seat of toClick) {
try {
await seat.click();
await new Promise((r) => setTimeout(r, 100));
} catch (e) {
clickSuccess = false;
break;
}
}
if (clickSuccess) {
selected = true;
console.log("seat selected successfully!");
break;
} else {
console.log("error while clicking");
}
}
}
6번째로 있던 234를 여기서 사용해봅시다. 사실 의미 없는 변수이긴 하지만, 같은 구역을 판단할 수 있을 것 같아 오류 방지용으로 가져온 변수입니다. 빈 자리들을 모아서, 각각을 돌면서 오름차순인지 확인합니다. 여기서 j = 0일때는 비교할 값이 없으므로(맨 앞에 좌석입니다) true로 넘어가줍니다.
모든 좌석이 오름차순이라면 저희가 클릭해야할 좌석이겠죠. 클릭해야할 좌석들을 모아서 클릭을 진행해줍니다. 클릭을 진행 한 후 결제 페이지로 넘어가봅시다.
3단계 : 결제
const fnSelectExists = await frmSeat.evaluate(
() => typeof fnSelect === "function"
);
if (fnSelectExists) {
try {
await frmSeat.evaluate(() => fnSelect());
isSeatSelected = true;
console.log("fnSelect called successfully!");
break;
} catch (e) {
console.log("fnSelect error:", e.message);
const fnRefreshExists = await frmSeat.evaluate(
() => typeof fnRefresh === "function"
);
if (fnRefreshExists) {
await frmSeat.evaluate(() => fnRefresh());
}
}
} else {
console.log("fnSelect not available");
const fnRefreshExists = await frmSeat.evaluate(
() => typeof fnRefresh === "function"
);
if (fnRefreshExists) {
await frmSeat.evaluate(() => fnRefresh());
}
}
결제하기까지 이미 선택된 좌석입니다.를 어떻게 처리해야할 지 모르겠어서 이렇게 처리했습니다. 우선 다음으로 넘어갈 버튼이 있는지 유무를 확인하고 클릭을 합니다. 문제가 없으면 좌석을 찾고 있는 While문을 탈출합니다. 문제가 생겼다면 새로고침 버튼을 클릭합니다. 버튼이 안보이는 경우에도 새로고침을 진행합니다.
이렇게 로그인 후 특정 시간에 대기열 진입, 보안 문자 인증 후 구역 선택을 하면 연석 선택 후 결제하기 페이지까지 이동했습니다. 다음 글에서는 넷퍼넬 분석을 해보도록 하겠습니다.
좋을 개발 도구나 자동화 아이디어가 있다면 댓글로 추천 부탁드립니다! 이제 어떤 걸 개발해볼 지 고민 중이라 여러분의 아이디어가 큰 도움이 될 것 같아요.
언제나처럼 — 시작은 삽질이지만, 끝은 지식입니다.
'자유로운 개발일지 > 실험일지' 카테고리의 다른 글
[직링 이해하기] 넷퍼넬 + 예매 버튼 활성화 (4) | 2025.07.13 |
---|---|
[직링] 기능 개발 (4) | 2025.07.03 |
[직링 이해하기] HTTP 요청 방식 (6) | 2025.07.02 |
[직링] 멜론 자동 호출 (9) | 2025.06.30 |
[직링] 캡챠 이미지 인식 (7) | 2025.06.29 |