서버에서 발급한 세션ID를 모든 요청마다 검증하는 방식
Request Header 확인:
Cookie: JSESSIONID=
왜 콘솔에서 스크립트 실행이 차단될까?
브라우저 콘솔에서 document.createElement('script')로 API를 호출하면 새로운 세션이 생성되어 기존 세션이 무효화
멜론티켓은 용도가 다른 두 개의 JSESSIONID를 사용
로그인 세션 (일반 웹)
Domain: ticket.melon.com
Path: /
용도: 일반 웹 브라우징, 페이지 상태 관리
로그인 시 함께 발급되는 쿠키들:
Domain: ticket.melon.com
Path: /reservation
# 일반 페이지 접속
GET https://ticket.melon.com/concert/index.htm
Cookie: JSESSIONID=ABC123 (Path=/ 쿠키만)
# 티켓팅 팝업 접속
GET https://ticket.melon.com/reservation/popup/onestop.htm
Cookie: JSESSIONID=ABC123; JSESSIONID=XYZ789
↑ Path=/ ↑ Path=/reservation
서버는 더 구체적인 Path의 쿠키를 우선적으로 사용
https://ticket.melon.com (메인)
서버 → Set-Cookie 응답
Cookie 설정:
- MLCP(Melon Login Credential Package): 인증 토큰 (JWT 와 같은 사용자 인증 정보)
- keyCookie: 사용자 고유 ID (간단 식별자)
- MAC(Message Authentication Code): 암호화된 인증 정보 (MLCP 위변조 검증)
- JSESSIONID (일반 세션, Path=/)

🎫 NetFunnel이란?
대기열 관리 솔루션: 한국에서 티켓팅/예매 사이트(멜론, 인터파크, 예스24, YES24 등)가 거의 다 사용하는 트래픽 제어 시스템
핵심 원리
서버 처리 용량: 초당 100명
실제 접속 시도: 초당 10,000명
→ NetFunnel 대기실에 9,900명 대기 → 선착순으로 100명씩 입장
동작 방법
ts.wseq 요청이 날아감NetFunnel_ID: 대기존 식별 (전역), TKT_POC_ID: 티켓팅 세션 식별 (티켓 도메인) 발급된 것 확인 가능Request (2초마다 반복):
GET https://nfp.melon.com/ts.wseq?opcode=5004&...
Cookie: NetFunnel_ID=WP15; TKT_POC_ID=WP15
Response Body:
NetFunnel.gControl.result='5002:200:key=...&nwait=14532&nnext=120...';
↑ 대기 인원 감소 중
Response Body:
NetFunnel.gControl.result='5002:200:key=...&nwait=0&nnext=0&complete=Y';
↑ 대기 완료!
이후:
window.location.href = '/reservation/popup/onestop.htm?...';
→ 좌석 선택 팝업으로 리다이렉트
🔐 NetFunnel 검증 로직
// 좌석 API 호출 시
@GetMapping("/tktapi/product/seat/seatMapList.json")
public ResponseEntity<?> getSeatList(HttpServletRequest request) {
// 1. NetFunnel 통과 확인
String nfId = getCookie(request, "NetFunnel_ID");
String tktPocId = getCookie(request, "TKT_POC_ID");
if (nfId == null || tktPocId == null || !nfId.equals(tktPocId)) {
return error("NF001", "대기열을 거치지 않은 접근입니다");
}
// 2. NetFunnel 서버에 검증 요청
boolean isValid = netfunnelService.validate(nfId, userId);
if (!isValid) {
return error("NF002", "유효하지 않은 대기 티켓입니다");
}
// 3. 대기 완료 시간 확인 (5분 이내만 유효)
long passedTime = netfunnelService.getPassedTime(nfId);
if (passedTime > 300000) { // 5분
return error("NF003", "대기 완료 후 시간이 초과되었습니다");
}
// 모든 검증 통과
return ResponseEntity.ok(seatData);
}
⚠️ NetFunnel 우회 불가능 이유
NetFunnel은 자체 서버에서 티켓 발급부터 완료까지 모든 상태를 관리하므로 클라이언트 조작으로는 우회 불가
팝업 URL:
https://ticket.melon.com/reservation/popup/onestop.htm?prodId=&scheduleNo=
이때 서버가 JSESSIONID 발급:
↓
서버 → Set-Cookie: JSESSIONID=
이 JSESSIONID가 좌석 API 호출 시 검증에 사용됨!
💡 왜 팝업마다 새 JSESSIONID를 발급할까 ?
보안 + 격리 목적
// 서버 로직 (의사코드)
// onestop.htm 요청 시
if (request.getRequestURI().contains("onestop.htm")) {
// 1. 기존 로그인 검증
if (!isLoggedIn(request.getCookie("MLCP"))) {
redirect("/login");
}
// 2. 티켓팅 전용 세션 생성
HttpSession ticketSession = request.getSession(true); // 새 세션 강제 생성
ticketSession.setAttribute("prodId", request.getParameter("prodId"));
ticketSession.setAttribute("scheduleNo", request.getParameter("scheduleNo"));
ticketSession.setAttribute("startTime", System.currentTimeMillis());
ticketSession.setMaxInactiveInterval(300); // 5분 타임아웃
// 3. JSESSIONID 쿠키로 반환
response.addCookie(new Cookie("JSESSIONID", ticketSession.getId()));
}
모든 좌석 관련 API는 티켓팅 세션을 검증
브라우저 → GET /tktapi/product/seat/seatMapList.json?blockId=
Cookie: JSESSIONID=
↓
서버 → 1. Cookie에서 JSESSIONID 추출
2. sessions.get("")
3. 존재하고 유효하면 → 데이터 응답
4. 없거나 만료되면 → {"code":"T9110", "message":"올바른 접근이 아닙니다"}
멜론티켓의 세션 관리는 3중 방어선으로 구성