srt_mobile_api.models
¶
요청 입력과 응답 결과의 값 타입 전부.
SRT 앱은 WebView 껍데기라 서버가 돌려주는 것이 대개 렌더링된 HTML 입니다.
srt_mobile_api.parsers 가 그 HTML 에서 뽑아낸 값이 여기 있는 frozen
dataclass 로 들어옵니다. 원본 페이지가 필요할 때를 위해 HtmlPage 계열이
따로 있습니다.
여기 있는 것은 값 객체일 뿐이라 아무것도 전송하지 않습니다. 상태를 바꾸려면
MutationConsent 로 범주를 열어야 합니다.
민감한 필드 — 카드번호, 카드 비밀번호, 생년월일, PNR, 쿠폰번호, NetFunnel
키 — 는 repr 에서 빠지므로 객체를 그대로 찍어도 값이 새지 않습니다.
전선에 실릴 값은 만들 때 검사합니다. 역코드·날짜·시각 같은 자리는 ASCII
숫자열이어야 하고(_is_digits), 코드값 셋(SrtTrainGroupCode,
SrtSeatAttrCode)은 Literal 별칭이면서 런타임에서도 다시 좁혀집니다.
INSTALLMENT_MONTH_OPTIONS
module-attribute
¶
SeatType
¶
Bases: Enum
예약할 좌석 등급 선호. srtgo 의 같은 열거형을 옮긴 것입니다(srt.py:413-417).
*_FIRST 는 한쪽을 먼저 보되 그 열차의 잔여석에 따라 다른 쪽으로 넘어가고,
*_ONLY 는 잔여석과 무관하게 한 등급만 고집합니다.
SrtSession
dataclass
¶
membership_number
property
¶
계정의 회원번호 — 로그인 응답 userMap 의 MB_CRD_NO 를 꺼내 줍니다.
카드결제 폼이 mbCrdNo 로 이 값을 싣기 때문에 필요합니다.
키가 없거나 문자열이 아니면 "" 입니다. 빈 값이 곧 "로그인하지 않음"이라는
것은 앱 자신의 관례입니다
(docs/analysis/full-api-analysis-2026-07-20.md:431).
근거의 출처가 반씩 다릅니다. mbCrdNo 라는 이름은 앱의 것이고
(ara0101v.js:319,321), 그것이 Ata09036 결제 본문에 실린다는 것은 참조
구현들의 실제 전송에만 근거가 있습니다
(card_payment_payload 참고).
PassengerCounts
dataclass
¶
PassengerCounts(adult=1, child=0, senior=0, disability_1_to_3=0, disability_4_to_6=0, infant=0, youth=0)
승차인원 — 앱의 일곱 가지 승객 유형. 그중 다섯만 자기 슬롯으로 나갑니다.
유아는 자기 psgTpCd 가 없어 어린이 슬롯에 접힙니다. 예약 페이지의
goRevFn 이 어린이 슬롯에 유아를 더하고 infantCnt 로 따로 선언합니다.
접힌 수는 child_slot_count 이고, child 와 구분됩니다.
청소년은 슬롯이 있습니다 — psgTpCd 6. commCode.js 어느 사본에도
없지만 공공할인 04 경로의 승차인원선택 popup에서 나타납니다.
Live-verified 2026-07-26: child=2,infant=3 → psgTpCd2="5", psgInfoPerPrnb2="5", infantCnt="3"; youth=1 → psgTpCd2="6". 청소년으로 실제 예약이 되는지는 미확인.
child_slot_count
property
¶
어린이 슬롯(psgTpCd 5)에 실리는 수 — 어린이 + 유아.
편의를 위한 합이 아니라 앱이 실제로 하는 접기입니다. 예약 페이지(goRevFn)도
할인 승차권 페이지(setPassenger_callback 의
parseInt(obj.passenger5) + parseInt(obj.passenger6))도 똑같이 더합니다.
어린이 없이 유아만 있어도 이 슬롯은 채워집니다. 앱의 검사가 합에 걸려
있으므로(if(passenger != '0')) PassengerCounts(adult=1, infant=1) 은
어린이 슬롯 1 과 infantCnt=1 을 함께 보냅니다. 페이지가 하는 그대로이고,
여기서 다듬지 않습니다.
total
property
¶
총 인원(totPrnb) — 유아까지 포함한 머릿수.
유아가 세어지는 것은 앱의 totPrnb += passenger 가 접기 뒤에 실행되기
때문입니다. 팝업의 setTotalPassenger 도 i=1..7 을 전부 더합니다.
따라서 getPsgTotCnt()(ara0101v.js:35)가 보장하는 등식은 실제로 나가는
슬롯들에 대해 totPrnb == sum(psgInfoPerPrnb…) 이고, 이 속성이 그 값입니다.
NetFunnelToken
dataclass
¶
TrainSearchQuery
dataclass
¶
TrainSearchQuery(departure_station_code, arrival_station_code, departure_date, departure_time='060000', passengers=PassengerCounts(), train_group_code='109', seat_attr_code='015', departure_station_name=None, arrival_station_name=None)
for_return_leg
¶
왕복의 오는열차 검색 질의를 만듭니다 — 출발역과 도착역을 뒤집습니다.
앱이 오는열차를 다시 검색할 때 하는 것과 같습니다. 역을 맞바꾸고
dptDt1/dptTm1 대신 back_dptDt1/back_dptTm1 로 찾습니다
(ara1001l.js:110-115, fn_search 의 fv_sRtnCd == "2" 가지). 인원·
열차그룹·좌석속성은 그대로 따라갑니다.
SRT 의 왕복은 요청 하나에 여정 둘이 아닙니다. 평범한 편도 검색 둘과 평범한
편도 예약 둘이며(reserve 참고), 두
번째를 "오는" 것으로 만드는 것은 역이 뒤집혀 있다는 사실과 양쪽 예약이
rtnDv=1 을 싣는다는 것뿐입니다.
departure_time 기본값이 "000000" 인 것도 앱을 따른 것입니다
(back_dptTm1 의 초기값, ara0101v.js:111). 돌아오는 날 자정부터 훑어야
후보가 다 보이기 때문입니다.
날짜·시각의 앞뒤는 검사하지 않습니다. 앱은 오는 날이 가는 날보다 이르거나
같은 날인데 시각이 이르면 막지만(ara0101v.js:593-603) 그것은 이 라이브러리가
그리지 않는 폼 위의 대화상자입니다.
TrainSummary
dataclass
¶
TrainSummary(train_no, train_group_code=None, service_class_code=None, run_date=None, departure_date=None, departure_time=None, arrival_date=None, arrival_time=None, departure_station_code=None, arrival_station_code=None, raw=dict(), departure_station_name=None, arrival_station_name=None, departure_run_order=None, arrival_run_order=None, seat_attr_code=None, run_time=None, train_run_order=None, departure_consist_order=None, arrival_consist_order=None, current_delay=None, expected_delay=None, general_seat_availability=None, special_seat_availability=None, reservation_wait_availability=None, standing_availability=None, general_seat_availability_name=None, special_seat_availability_name=None, reservation_wait_availability_name=None, standing_availability_name=None, received_amount=None, discount_rate=None, received_fare=None, train_composition_codes=(), train_class_code=None, general_class_discount_rate=None, special_class_discount_rate=None)
TransferItinerary
dataclass
¶
환승 여정 — 선행 열차와 후행 열차를 한 쌍으로 묶은 것.
SRT 의 환승은 여정 슬롯 두 개를 실은 예약 하나입니다. 앱의 환승 토글이
jrnyTpCd="14"(환승편도)와 jrnyCnt="2"를 한 번에 씁니다
(ara0101v.js:302-303). jrnyCnt="2" 를 쓰는 자리는 번들 전체에서 여기
하나뿐이라 그 값은 곧 환승을 뜻합니다. 왕복은 "1" 그대로에 예약이 둘입니다.
first_leg 가 선행(jrnySqno1="001"), second_leg 가
후행(jrnySqno2="002")입니다(ara0101v.js:97).
이 타입이 검색 결과의 절반짜리 행을 예약 폼에 넘기는 실수를 방지합니다.
reserve_transfer 는 이 타입만 받습니다.
생성 시점 검사: 두 열차 모두 TrainSummary, 선행 도착역 == 후행 출발역,
후행이 선행 도착보다 먼저 출발 불가, 같은 열차 불가.
환승 예약은 2026-07-31 실서버에서 확인됐습니다 — 동대구→목포, 열차 304 + 653,
오송 환승, SUCC/IRR000018. 단, 조회 결과에는 구간이 코레일 열차인 여정이
섞여 오고 그런 여정은 예약 폼이 거절합니다. 양쪽
TrainSummary.service_class_code 가 "17" 인지 보고 고르십시오.
UnpairedTransferGroup
dataclass
¶
환승 검색이 준 행 중 여정으로 묶이지 못한 것들.
버리지 않고 남깁니다. 쌍을 짓는 것은 서버가 아니라 이 라이브러리의 추론입니다
— 서버는 납작한 행 목록을 주고 trnOrdrNo 로 묶는 것은 이쪽입니다. 추론이 맞지
않을 때 그 행들은 호출자가 볼 수 있는 곳에 있어야 하기 때문입니다.
reason 은 코드가 아니라 문장입니다. 실제로 관측한 적 없는 상황들이라 코드
어휘를 만들면 지어낸 것이 됩니다.
TrainSearchMetadata
dataclass
¶
TrainSearchMetadata(message_code, status, query_count, has_following_page=None, message='', raw=dict())
TrainSearchResult
dataclass
¶
TransferSearchResult
dataclass
¶
환승 검색 결과 — 묶인 여정, 남은 행, 그리고 손대지 않은 원본.
응답 모양은 확인됐습니다. 환승 검색은 구간마다 한 행씩을 평범한 dsOutput1
목록에 담아 주고 — 직통 행과 열 구성이 같습니다 — 한 여정에 속하는 행들은
trnOrdrNo 를 공유합니다. 동대구(0015) -> 광주송정(0036) 에서 서버가 준 10 행이
그랬습니다: trnOrdrNo=1 은 382 열차 동대구->오송 과 411 열차 오송->광주송정
하는 식입니다. 모든 행이 chtnDvCd="2" 를 달고 있었고 fllwPgExt2 는
null 이었습니다.
즉 여기서 trnOrdrNo 는 목록 안의 열차 순번이 아니라 여정 번호입니다.
...2 로 끝나는 열들도 모든 행에 있기는 하지만 전부 빈 문자열입니다
(trnNo2: "", dptRsStnCd2: "", jrnySqno: "") — 후행 구간은 열이 아니라
별도의 행이기 때문입니다.
itineraries 는 깨끗이 묶인 것, unpaired 는 묶이지 못한 것이며
각각 이유가 붙어 있습니다. 여정 개수만으로는 빠진 것이 있는지 알 수 없으니
빠짐없이 보려면 그쪽도 읽어야 합니다. search 는 손대지 않은
TrainSearchResult 라 원본 행과 원본 JSON 으로 언제든 되돌아갈 수
있습니다.
ReservationRecord
dataclass
¶
ReservationRecord(pnr_number, journey_list_key, arrival_date, arrival_station_code, arrival_time, delay_acceptance_flag, departure_date, departure_station_code, departure_time, lump_settlement_target_number, provisional_settlement_target_flag, service_class_code, total_seat_count, train_group_code, train_number, raw=dict())
lump_settlement_target_number
class-attribute
instance-attribute
¶
ReservationTrain
dataclass
¶
ReservationAttemptResult
dataclass
¶
ReservationAttemptResult(message_code, status, total_received_amount, reservation, train, message, temporary_job_sequence, command, raw)
temporary_job_sequence
class-attribute
instance-attribute
¶
SrtReservationHold
dataclass
¶
예약은 됐지만 아직 결제되지 않은 상태 — 실제로 전송된 reserve 의 결과.
핵심은 pnr_no 입니다. 뒤이어 취소하거나 결제할 때 이 예약을 가리키는
이름이고, srtgo 도 성공한 예약에서 같은 값을 챙깁니다(reservListMap[0].pnrNo,
srt.py:1006). pnr_no 와 journey_list_key 는 비밀에 준하므로
repr 에 나오지 않습니다 — 값을 보려면 속성을 직접 읽어야 합니다.
dry-run 예약은 이것을 만들지 않습니다. 그쪽은
MutationPreview 를 돌려줍니다.
journey_list_key
class-attribute
instance-attribute
¶
SrtReservationSummary
dataclass
¶
SrtReservationSummary(pnr_no, received_amount=None, ticket_special_number=None, seat_number=None, service_class_code=None, train_no=None, departure_date=None, departure_time=None, departure_station_code=None, arrival_time=None, arrival_station_code=None, payment_limit_date=None, payment_limit_time=None, settlement_flag=None, raw_train=dict(), raw_pay=dict())
One row of the 예약/발권 목록 (/atc/selectListAtc14016_n.do).
한 행은 서버가 따로 보낸 두 목록을 인덱스로 짝지어 만듭니다 —
trainListMap[i](여정 정보)와 payListMap[i](결제 상태). srtgo 도 이
엔드포인트를 그렇게 읽습니다(srt.py:1069-1082).
필수인 것은 pnr_no 하나입니다.
cancel 과 scripts/recover_hold.py 가
쓰는 식별자입니다. 나머지는 전부 선택이고 기본값이 None 인데, 행이 채워진
응답을 본 적이 없기 때문입니다 — 확인에 쓴 계정에 예약이 없어서
trainListMap/payListMap 이 빈 배열로만 왔습니다.
그래서 아래 필드명의 출처가 갈립니다. payListMap, tkSpecNum, iseLmtTm,
stlFlg 는 v2.0.41 번들에서 0회이고 srtgo 의 실제 전송 기록에만 있습니다.
iseLmtDt, rcvdAmt, seatNum, pnrNo, rsvChgTno, jrnyCnt 는
승차권 확인 페이지의 인라인 스크립트에 나옵니다(gotoDetailARD02018(...),
gotoDetailARD0201V(...)) — 이름은 앱이 뒷받침하지만 그것이 위 두 목록 중
어디에 들어가는지는 아닙니다.
따라서 None 은 "그 예약에 그런 값이 없다"가 아니라 "서버가 이 이름으로 보내지
않았다"로 읽어야 합니다. 실제로 온 것은 raw_train / raw_pay 에
있습니다.
SrtReservationListResult
dataclass
¶
SrtReservationListResult(reservations, status, message_code='', message='', row_count=None, total_page_count=None, raw=dict())
파싱된 예약/발권 목록과 서버가 함께 보낸 봉투.
예약이 없는 계정은 빈 reservations 이지 예외가 아닙니다. 빈 열차
검색과 다릅니다 — 검색은 없음을 strResult=FAIL / WRG000000 으로
선언하지만, 이 엔드포인트는 resultMap[0].strResult="SUCC" / IRZ000005 /
"조회할 자료가 없습니다." 에 진짜로 빈 배열을 줍니다. 같은 응답의 두 번째 봉투
(rsMap)는 FAIL 이라고 말하는데 그것을 실패로 읽지 않는 이유는
parse_reservation_list_response 에 있습니다.
row_count / total_page_count 는 rowCnt / totPageCnt 이며
JSON 정수로 옵니다(빈 응답에서는 둘 다 0). 없으면 None 입니다.
pnr_numbers
property
¶
목록에 든 PNR 전부, 서버가 준 순서대로.
PNR 을 잃어버렸을 때 행 구조를 몰라도 되찾을 수 있게 하는 것이 용도입니다.
scripts/recover_hold.py --list 가 출력하는 것이 정확히 이 값입니다.
SrtCancelResult
dataclass
¶
미결제 예약취소의 응답 봉투.
SRT 의 일반적인 resultMap 봉투이고 strResult == "SUCC" 면 성공입니다. 여정
한 건짜리 미결제 예약을 실제로 취소했을 때 SUCC / IRG000000 이 왔습니다.
업무상 실패는 예외가 아니라 데이터로 옵니다 — succeeded 가 False
이고 서버의 message_code 가 그대로 실립니다.
message 와 raw 는 원본 봉투가 예약 식별자를 되울려 주므로 repr
에서 빠집니다.
경로 자체는 srtgo 에서 왔고 v2.0.41 번들에서 0회입니다. 봉투 처리를 느슨하게 둔
이유는 parse_unpaid_cancel_response 에 있습니다.
SrtPaymentCard
dataclass
¶
SrtPaymentCard(card_number, card_password, card_validation_number, card_expire_date, installment_months=0, card_type='J')
카드결제가 실어 보내는 카드 필드 — 나가기 전에 모양을 검사합니다.
여기 넣은 값은 실제로 전송될 수 있습니다. payment 는 라이브 범주이므로
pay_with_card 에 dry_run=False 와
분명한 카드 종류 주장이 붙으면 이 카드로 청구됩니다. 기본값은 마스킹된
미리보기이고, CARD_SECRET_FIELDS 가 결제 경로가
아닌 모든 경로에서 이 네 이름을 막습니다.
필드가 무엇을 담는지가 이름만으로는 헷갈리는 자리입니다.
card_password는 카드 비밀번호 앞 두 자리입니다. 전체가 아닙니다.card_type이"J"(개인)냐"S"(법인)냐가card_validation_number의 뜻을 결정합니다 —"J"면YYMMDD생년월일,"S"면 열 자리 사업자등록번호입니다.card_expire_date는YYMM.
비밀 네 개(card_number, card_password,
card_validation_number, card_expire_date)는 repr=False 인
동시에 각자의 이름으로 SENSITIVE_KEYS 에 올라
있어서 repr() 과 redact_value 양쪽에서
가려집니다. 패턴이 아니라 이름으로 가리는 이유는 CARD_RE 가 13~19 자리
숫자열만 잡아서 두 자리 비밀번호도, YYMM 도, YYMMDD 도 다 빠져나가기
때문입니다.
installment_months 와 card_type 은 비밀이 아니라 일부러 가리지
않습니다. 다만 이 둘의 전송용 이름 ismtMnthNum1/athnDvCd1 은
SENSITIVE_KEYS 에 있으므로
MutationPreview 에서는 가려집니다.
카드번호가 진짜인지는 검사하지 않습니다. 자릿수와 숫자 여부만 봅니다. Luhn 검사를 넣으면 합성 테스트 카드를 만들 수 없게 되고, 무엇보다 이 라이브러리가 어떤 카드번호가 유효한지 알려 주는 물건이 됩니다.
card_validation_number
class-attribute
instance-attribute
¶
SrtPaymentResult
dataclass
¶
카드결제의 응답 봉투.
succeeded 와 failed 는 서로의 부정이 아닙니다. 그것이 이 타입의
요점입니다. 결제에서는 양쪽 오판이 다 비싸므로 모르는 상태값에 대해 판단하지
않습니다.
"SUCC" 도 "FAIL" 도 아닌 상태는 모름입니다. raw 를 읽고, 카드가
실제로 청구됐는지 다른 경로로 확인해야 하며, 무턱대고 재시도하면 안 됩니다 —
다시 보낸 결제는 두 번 청구될 수 있습니다.
확인된 두 가지는 실제 결제의 SUCC / IRT000000 과 가짜 카드의 FAIL /
WRT100170 입니다. 경로 Ata09036 은 v2.0.41 오프라인 디컴파일 21,673 개 파일
전체에서 0회이고 앱은 이 경로를 쓰지 않습니다 — 경로도 본문도 이 봉투 모양도 참조
구현들의 실제 전송에만 근거가 있습니다
(parse_card_payment_response 참고).
SrtRefundTicketInfo
dataclass
¶
SrtRefundTicketInfo(pnr_no, sale_date='', sale_window_number='', sale_sequence_number='', return_password='', buyer_name='', raw=dict())
환불에 필요한 발권 승차권 식별정보 — 환불 1단계의 결과.
POST /atc/getListAtc14087.do(본문 없음, PNR 은 Referer)의
outDataSets.dsOutput1[0] 에서 읽습니다 — 여기만 dsOutput1 이고, 다른 읽기는
전부 dsOutput0 을 씁니다.
return_password 가 환불을 승인하는 자격증명이라 repr 에서 빠지고,
buyer_name·pnr_no 와 함께
redact_payload 에서도 가려집니다. 판매 식별자 셋
(sale_date, sale_window_number, sale_sequence_number)은
일부러 가리지 않습니다 — 비밀번호가 가려진 이상 그것만으로는 아무것도 승인하지
못하기 때문입니다.
이 경로는 참조 구현 하나에만 있습니다. 자세한 내력은
parse_refund_ticket_info_response 참고.
return_password
class-attribute
instance-attribute
¶
SrtRefundResult
dataclass
¶
발권된 승차권 환불의 응답 봉투.
봉투는 SRT 의 평범한 resultMap SUCC/FAIL 입니다 — 예약취소와 같고, 결제의
outDataSets.dsOutput0 과는 다릅니다.
업무상 실패는 예외가 아니라 값으로 돌아옵니다. 실제 승차권 환불에서 SUCC /
IRT200277 이 왔습니다.
경로는 참조 구현 하나에만 있고, v2.0.41 오프라인 디컴파일 21,673 개 파일 전체에서 0회입니다.
HtmlPage
dataclass
¶
SeatCarOption
dataclass
¶
좌석 페이지가 고르라고 내놓은 호차 하나와 그 호차의 잔여석 수.
페이지의 <select id="selectScarNo"> 에서 읽습니다 — 좌석 페이지가 들고 있는
재고 정보는 그것뿐입니다. 좌석배치도는 이 응답에 없습니다. 실제 페이지도
<div id="trnScarSeatInfo"> 를 비워 두고, 호차를 고른 뒤에 배치도를 따로
가져옵니다(get_seat_grid).
SeatSelectionPage
dataclass
¶
SeatGridSeat
dataclass
¶
좌석배치도의 좌석 한 칸 — 그 좌석의 이름 두 개를 다 들고 있습니다.
/arc/selectListArc02011_n.do 가 주는 배치도의 각 칸이 이런 요소입니다::
<div role="text" tabindex="0" aria-label="1C" id="scarSeat_1C"
class="seatChoice015Y"
onclick="choiceSeatNo('3', '1C', 'Y');"><span>1C</span></div>
좌석에는 이름이 둘 있고 서로 바꿔 쓸 수 없습니다. choiceSeatNo 의 첫 인자는
호차 안에서 1, 2, 3, 4, 5 … 로 이어지는 내부 좌석번호
(internal_seat_number)이고, 둘째 인자는 승객이 좌석에서 읽는 인쇄
좌석명(printed_seat_label, 1A, 1B, 1C, 1D, 2A …)입니다. 위 칸에서 내부
3 번의 인쇄명이 1C 입니다. 요소의 id 와 aria-label 은 인쇄명으로
만들어집니다.
예약 폼이 받는 것은 인쇄 좌석명입니다. 앱의 좌석선택 콜백은
{scarSeatNo: "2,7,10", scarSeatNm: "1B,2C,3B", scarNo: 1} 로 둘 다 받아 놓고,
seatNo1_1..N 을 인쇄명 쪽인 scarSeatNm 으로 채우며 scarSeatNo 는
버립니다(ara0101v.js:868-878). 이것은 번들 근거이고 실제 전송으로 확인된 것은
아닙니다(personal_reservation_payload 참고).
seat_attribute_code 는 칸의 class(seatChoice<코드><Y|N>)에 박힌
좌석속성코드입니다. 한 호차 안에 000, 015, 021, 028 이 모두
나타났습니다. selectable 은 choiceSeatNo 의 셋째 인자가 Y 인지이며,
N 칸도 그려지지만 고를 수 없습니다.
SeatDesignation
dataclass
¶
호차 하나에서 호출자가 고른 좌석들 — 낱개가 아니라 묶음으로 검사됩니다.
reserve(..., designated_seats=…) 가 받는 타입입니다. 손으로 만들지 말고
SeatGrid.choose 로 배치도에서 만드는 편이 좋습니다 — 그래야 좌석명이 서버가
준 것이고 선택 가능 여부도 대조할 대상이 있습니다.
생성 시점에 다음을 검사합니다.
- 좌석이 최소 하나, 전부 정확히
SeatGridSeat, - 모든 좌석이
selectable—N칸은 서버가 그려는 주지만 받지는 않습니다, - 같은 인쇄 좌석명이 두 번 나오지 않음(한 좌석이 인원 슬롯 둘을 먹고 한 사람이 좌석 없이 남습니다),
- 호차 번호가 숫자만 —
scarNo1로 그대로 나가는 값입니다.
인원수와 좌석수가 맞는지는 여기서 보지 않습니다. 그 검사는 승차인원을 알아야
하므로 양쪽이 다 보이는
personal_reservation_payload 에 있습니다.
SeatGrid
dataclass
¶
Bases: HtmlPage
열차 하나, 호차 하나의 파싱된 좌석배치도.
car_number 는 요청할 때 지목한 호차입니다. 이 라이브러리가 scarNo
로 보낸 값이지 응답에서 읽은 값이 아닙니다 — 배치도 조각은 자기가 몇 호차인지
말하지 않습니다. seats 는 고를 수 있든 없든 문서 순서대로의 모든 칸입니다.
좌석을 지정하려면 choose 로 SeatDesignation 을 만들면 됩니다.
choose
¶
인쇄 좌석명("1C", "2A" …)으로 좌석을 지정합니다.
승객이 좌석에서 읽는 이름이자 예약 폼이 실제로 실어 보내는 이름이라
(ara0101v.js:868-878) 이 인자를 받습니다. 내부 좌석번호가 아닙니다.
없는 좌석명은 조용히 버리지 않고 ValueError 입니다. 오타 하나가
일행의 좌석 수를 소리 없이 줄이지 않게 하려는 것이고, 오류 메시지에 고를 수
있는 좌석명이 함께 나옵니다.
PublicDiscountEntitlement
dataclass
¶
공공할인 한 종류와, 이 계정이 그것을 승인받았는지.
code 는 PBL_DISC_CD("01"~"08")이고, name 은
public_discount_name 이 풀어 준 이름입니다.
07, 08 은 페이지에 분기는 있는데 이름이 어디에도 없어서 "" 입니다.
코드와 슬롯의 대응은 추론입니다. 페이지는 data1Check 부터 data8Check
까지 여덟 개의 플래그를 렌더링해 주면서 그 옆에 PBL_DISC_CD 를 한 번도 적지
않습니다. 둘을 잇는 근거는 두 플래그를 한꺼번에 읽는 유일한 분기에 페이지가 직접
단 주석뿐입니다 — if(data1Check == "Y" && data2Check == "Y") 위의
"다자녀(01)와 임산부(02)가 신청이 승인된 경우". 플래그도 코드도 정확히 여덟
개이고, 아래쪽의 여덟 개 PBL_DISC_CD 분기가 01~08 순서로 늘어서
있습니다. 나머지 여섯의 대응은 이것과 어긋나는 근거도, 뒷받침하는 근거도 없습니다.
PublicDiscountPage
dataclass
¶
Bases: HtmlPage
할인 승차권 페이지를 "이 계정이 어떤 공공할인을 가졌나"로 읽은 것.
아무것도 승인받지 못한 계정은 오류가 아니라 답입니다. 그 경우 페이지는
Sr.msgs.notice006("할인승차권은 홈페이지를 통해 인증된 고객만 이용이
가능합니다…")을 띄우고 메인으로 돌려보내지만, "가진 것이 없다"가 곧 물어본 것에
대한 답이라 그대로 돌려줍니다 — is_eligible 이 False 이고
entitlements 의 모든 행이 approved=False 입니다.
담지 않는 것: 승인된 할인의 관리번호(PBL_DISC_MG_NO)와 확인·만료 날짜.
그 값들은 페이지의 if/else if 분기 본문 안에 서버가 렌더링해 넣는데, 읽을 수
있었던 계정에서는 여덟 분기가 전부 비어 있었습니다. 승인된 계정을 가진 호출자는
raw 에서 직접 읽을 수 있습니다.
PublicDiscountSelection
dataclass
¶
어떤 공공할인으로 검색할지 — 코드와 승인번호.
할인 승차권 검색에서 호출자가 채우는 쪽입니다. ajax 폼이 pblDiscCd 와
pblDiscMgNo 로 싣는 두 값이고, 함께 가는 tgtDtrmYn 은 페이지가 늘 "Y"
로 두므로 선택지로 두지 않았습니다.
PublicDiscountEntitlement 와 타입을 나눈 것은 답하는 질문이 다르기
때문입니다 — 자격은 계정이 가진 것이고, 선택은 이번 검색이 무엇을 위한
것인지입니다.
code 는 "01"~"08" 의 PBL_DISC_CD 이고
public_discount_name 이 그중 여섯의 이름을
압니다.
management_no 는 값을 본 적이 없습니다. PBL_DISC_MG_NO 는 서버가
발급하는 승인번호로, 할인 승차권 페이지가 계정이 승인받은 할인의 분기 본문에
렌더링해 넣습니다. 승인이 없는 계정에서는 그 분기 본문이 전부 비어 있어 기본값이
"" 입니다 — 승인된 계정을 가진 호출자는 자기 PublicDiscountPage.raw
에서 읽어 넘기면 됩니다. 서버가 이 값을 요구하는지, 아니면 세션에서 알아내는지는
모릅니다.
인원수 제한은 여기서 검사하지 않습니다. 그것은 (코드, 인원) 쌍에 관한 사실이라
public_discount_search_payload 에 있습니다.
DiscountCoupon
dataclass
¶
DiscountCoupon(coupon_number='', discount_kind='', discount_rate='', validity='', basis='', remaining_uses='')
계정이 가진 할인쿠폰 한 장 — 페이지가 찍어 준 글자 그대로.
모든 필드가 표시용 문자열이지 파싱된 값이 아닙니다. discount_rate 는
"45%" 이고 remaining_uses 는 "이용가능 횟수 : 1" 입니다. 숫자를
원하면 호출자가 직접 뽑아야 합니다.
그렇게 둔 이유는 채워진 행을 본 적이 없어서입니다. 빈 상태("보유한 쿠폰이 없습니다.")는 확인됐지만 쿠폰을 가진 계정이 없었습니다. 필드명의 근거는 쿠폰 페이지에 주석으로 남은 디자이너 템플릿이 전부입니다 — class 이름은 페이지의 것이고 값은 예시입니다::
<span class="rate">45%</span>
<span class="boarding">탑승일기준</span>
<span class="date">2099.01.01 ~ 2099.12.31</span>
<span class="type"> 운임할인</span>
<span class="num">9910000001</span>
<span class="useCnt">이용가능 횟수 : 1</span>
discount_kind 는 페이지가 맨 위에서 밝히는 두 갈래입니다 — 운임할인은
운임을, 특실할인은 특실 추가금을 깎습니다.
DiscountCouponList
dataclass
¶
SrtCouponRegistrationRequest
dataclass
¶
할인쿠폰 등록 한 건 — 쿠폰번호와 쿠폰 비밀번호.
이 둘은 무기명 자격증명입니다. 번호와 비밀번호를 내미는 사람이 쿠폰을 쓰게
되고, 등록하는 계정은 그저 먼저 물어본 쪽일 뿐입니다. 그래서 두 속성명과 두 전송용
이름(dscp_no/dscp_pwd)이 모두
SENSITIVE_KEYS 에 있고, coupon_number
도 비밀번호와 나란히 repr=False 입니다. 일반 규칙으로는 둘 다 걸리지 않습니다:
dscp_pwd 는 문자 그대로의 password 가 아니고, 열 자리 dscp_no 는
CARD_RE 가 잡기에 짧습니다.
아래 두 제약은 /apa/selectListApa03020_n.do 페이지의 것입니다.
coupon_number—<input type="number" name="dscp_no" maxlength="10">에 숫자가 아닌 것을 지우고 열 자리로 자르는keyup핸들러가 붙어 있습니다. 즉 숫자만, 1~10 자리.coupon_password—<input type="password" name="dscp_pwd" maxlength="4">. 네 글자까지이고 숫자로 제한하지 않습니다. 페이지가 강제하지 않는 문자 집합을 넘겨짚으면 앱이라면 받았을 쿠폰을 거절하게 됩니다.
빈 값은 페이지가 거절하는 것과 같은 이유로 여기서 거절합니다
(mysrt006/mysrt007, js/common/messages.js:111-112).
SrtCouponRegistrationResult
dataclass
¶
The parsed envelope of a 할인쿠폰 등록.
이 응답을 본 적이 없습니다. 아래 필드는 전부 쿠폰 페이지의 성공 핸들러에서 읽은 것이고, 이 라이브러리가 쿠폰 등록을 보낸 적은 없으며, 경로는 v2.0.41 오프라인 번들에서 0회입니다::
var msg = args.resultMap[0].MSG;
var rtncd = args.resultMap[0].RTNCD;
if(rtncd == "N"){ ...show msg... } else { ...show mysrt008... }
봉투가 다릅니다. 다른 상태변경이 모두 strResult/msgCd/msgTxt 를
돌려주는 데 반해 이쪽은 resultMap[0] 에 대문자 RTNCD/MSG 입니다. 익숙한
모양으로 바꾸지 않고 페이지가 읽는 그대로 두었습니다.
succeeded 도 페이지의 극성을 그대로 따릅니다. "N" 만 실패이고 나머지는
전부 성공입니다. 그래서 파서가 비어 있거나 없는 RTNCD 를 따로 거절합니다. 이
규칙대로면 코드가 없는 응답이 성공으로 읽히기 때문입니다.
여기서 성공은 요청이 접수됐다는 뜻이지 쿠폰이 보인다는 뜻이 아닙니다. 페이지의
성공 문구가 그렇게 말합니다(mysrt008: "쿠폰등록 요청을 완료하였습니다 …
쿠폰등록이 지연 될 경우 입력하신 쿠폰이 바로 조회 되지 않을 수 있습니다"). 등록
직후 get_discount_coupons 를 불러 아무것도
보이지 않는 것은 정상일 수 있습니다.
message 와 raw 는 거절된 쿠폰번호가 되울려 나올 수 있어 repr
에서 빠집니다.