콘텐츠로 이동

설정

이 가이드는 KorailConfig로 클라이언트의 동작을 바꾸는 방법을 설명합니다. 설정 필드 전체, DynaPath 토큰, 대기열(NetFunnel), 언어 필드, 환경변수로 설정을 만드는 방법을 차례로 다룹니다. 설정을 넘기지 않으면 코레일+ 앱 7.0.8에서 확인한 기본 요청값을 쓰며, 대부분은 바꿀 필요가 없습니다.

설정 적용

KorailConfig는 바꿀 수 없는(frozen) dataclass입니다. 바꿀 필드만 키워드 인자로 넘겨 만들고, KorailClient의 첫 번째 인자로 넘깁니다. 설정은 클라이언트를 만들 때 적용됩니다. 설정을 바꾸려면 새 설정으로 새 클라이언트를 만드세요.

from korail_mobile_api import KorailClient, KorailConfig

config = KorailConfig(timeout=30.0, netfunnel_wait_limit=120.0)
client = KorailClient(config)
try:
    calendar = client.get_train_calendar()
finally:
    client.close()

이미 있는 설정에서 몇 필드만 바꾸려면 dataclasses.replace를 씁니다. DynaPath 기기값은 원래 설정의 값을 그대로 이어받습니다.

import dataclasses

from korail_mobile_api import KorailConfig

base = KorailConfig()
longer_wait = dataclasses.replace(base, netfunnel_wait_limit=300.0)

설정 필드

이름 타입 기본값 설명
base_url str "https://smart.letskorail.com" KORAIL API 서버 주소입니다. 검사하지 않고 그대로 씁니다.
device str "AD" 공통 필드 Device의 값입니다.
version str "250601003" 공통 필드 Version의 값입니다.
app_version str | None "7.0.8" 별도 공통 필드 AppVersion의 값입니다. None이면 이 필드만 생략합니다.
key str "korail1234567890" 공통 필드 Key의 값입니다.
timeout float 60.0 API 요청의 제한 시간(초)입니다.
user_agent str "korailtalk" API 요청의 User-Agent 헤더입니다.
dynapath DynapathConfig 설정마다 새로 합성 DynaPath 토큰 설정입니다. DynaPath를 참고하세요.
device_width int 1440 공통 코드 조회(로그인 포함)의 deviceWidth 값입니다.
device_height int 3120 공통 코드 조회(로그인 포함)의 deviceHeight 값입니다.
android_sdk_int int 37 공통 코드 조회(로그인 포함)의 OSVersion 값입니다.
advertising_id str "" 승차권 목록 조회(get_ticket_list)의 txtDeviceId 값입니다. 빈 문자열이면 보내지 않습니다.
netfunnel_url str "https://nf.letskorail.com" 대기열 서버 주소입니다. 검사하지 않고 그대로 씁니다.
netfunnel_timeout float 3.0 대기열 요청 하나의 제한 시간(초)입니다.
netfunnel_user_agent str "Dalvik/2.1.0 (Linux; U; Android 17; SM-S948N Build/CP2A.260605.016)" 대기열 요청의 User-Agent 헤더입니다.
netfunnel_enabled bool True 대기열을 거칠지 여부입니다.
lang str | None None 공통 필드 lang의 값입니다. None이면 보내지 않습니다.
netfunnel_wait_limit float | None None 대기열에서 기다릴 누적 시간의 상한(초)입니다. None이면 제한하지 않습니다.
netfunnel_actions Mapping[str, str] | None None 관문별 대기열 식별값을 바꿉니다.
disable_dynapath bool False True이면 DynaPath 토큰을 붙이지 않습니다.

device, version, app_version, key, user_agent를 바꾸면 앱과 다른 요청이 됩니다. 기존 Version과 새 AppVersion은 서로 다른 필드입니다. API 요청의 User-Agent와 대기열 요청의 User-Agent는 서로 다른 설정입니다.

실제 기기값을 코드에서 관리한다면 KorailDeviceProfile과 build_config_from_profile을 사용할 수 있습니다. 이 함수는 DynaPath 식별자·모델·OS, 대기열의 Dalvik User-Agent, SDK·화면 크기를 같은 프로파일에서 설정합니다. API User-Agent는 관측된 korailtalk로 둡니다. 기존 설정의 나머지 값은 base=로 유지할 수 있지만, 사용자 정의 DynaPath 토큰 공급자나 DynaPath 비활성화 설정은 덮어쓰지 않고 거절합니다. 간편 API 예제를 참고하세요.

DynaPath

DynaPath는 앱이 보호하는 경로에 붙이는 토큰 헤더(x-dynapath-m-token)입니다. 기본으로 켜져 있습니다. 토큰은 기기 식별자, 앱 시작 시각, 안드로이드 버전, 기기 모델 같은 기기값과 요청 시각으로 만듭니다.

토큰을 붙이는 경로는 DynapathConfig의 allowlist_paths이며, 기본값은 다음 여섯 경로입니다. 앱이 토큰을 붙이는 경로 목록과 같은지는 앱 내부 값이 공개돼 있지 않아 확인하지 못했습니다.

  • /classes/com.korail.mobile.login.Login
  • /classes/com.korail.mobile.seatMovie.ScheduleView
  • /classes/com.korail.mobile.seatMovie.ScheduleViewSpecial
  • /classes/com.korail.mobile.trn.prcFare.do
  • /classes/com.korail.mobile.certification.TicketReservation
  • /classes/com.korail.mobile.nonMember.NonMemTicket

기본 기기값

dynapath를 지정하지 않으면 KorailConfig를 만들 때마다 기기값을 새로 합성합니다. 기기 식별자는 16자리 16진수 무작위 값, 앱 시작 시각은 설정을 만든 시각, 안드로이드 버전은 "17", 기기 모델은 "SM-S948N"입니다. 같은 설정 객체를 쓰는 동안에는 같은 기기값을 씁니다. 실제 기기의 값을 쓰려면 환경변수로 설정 만들기를 참고하세요.

직접 지정하기

DynapathConfig(enabled=True, ...)를 dynapath로 넘기면 그 설정을 그대로 씁니다. 켜진 DynapathConfig에는 token_settings와 token_provider 중 정확히 하나를 넣어야 하며, 그렇지 않으면 ValueError가 발생합니다.

DynapathConfig 필드 타입 기본값 설명
enabled bool False 토큰을 붙일지 여부입니다. KorailConfig에 직접 넘길 때는 True로 만듭니다.
token_settings DynapathTokenSettings | None None 라이브러리가 토큰을 만들 때 쓰는 기기값입니다.
token_provider DynapathTokenProvider | None None 토큰을 직접 만들어 주는 함수입니다.
timestamp_ms_provider Callable[[], int] | None None token_settings로 토큰을 만들 때 쓰는 시각(밀리초) 공급 함수입니다. None이면 현재 시각입니다.
random_text_provider Callable[[], str] | None None token_settings로 토큰을 만들 때 쓰는 무작위 문자열 공급 함수입니다. None이면 무작위로 만듭니다.
header_name str "x-dynapath-m-token" 토큰 헤더 이름입니다.
allowlist_paths frozenset[str] 위의 여섯 경로 토큰을 붙일 경로입니다.
device_name str "SM-S948N" token_provider에 넘기는 기기 이름입니다.
os_version str "17" token_provider에 넘기는 안드로이드 버전입니다.

DynapathTokenSettings는 다섯 값을 반드시 받습니다. 비어 있거나 공백뿐이면 ValueError가 발생하며, app_start_ts는 정수로 읽을 수 있어야 합니다. 나머지 필드에는 라이브러리 기본값이 있으며, 바꾸면 앱과 다른 토큰이 됩니다.

필드 뜻
device_id 기기 식별자입니다.
as_value 앱 서명에서 나온 값입니다.
app_start_ts 앱 시작 시각(밀리초 단위 epoch)을 숫자 문자열로 넣습니다.
os_version 안드로이드 버전입니다. 예: "17"
device_model 기기 모델명입니다. 예: "SM-S948N"

token_provider는 토큰을 붙이는 경로의 요청마다 DynapathRequestContext(method, path, url, device, version, key, user_agent, device_name, os_version)를 받아 토큰 문자열을 돌려주는 함수입니다. None을 돌려주면 헤더를 붙이지 않습니다. 함수가 예외를 발생시키면 요청을 보내지 않고 KorailProtocolError를 발생시킵니다.

끄기

disable_dynapath=True로 끕니다. 켜진 DynapathConfig를 함께 넘기면 ValueError가 발생합니다. 꺼진 DynapathConfig(enabled=False, ...)만 넘기는 것으로는 끌 수 없습니다. 기본값과 다른 꺼진 설정을 넘기면 ValueError가 발생하고, 기본값과 같으면 기기값을 합성해 켭니다.

from korail_mobile_api import KorailClient, KorailConfig

client = KorailClient(KorailConfig(disable_dynapath=True))

이미 있는 설정을 dataclasses.replace로 끌 때는 dynapath=DynapathConfig()도 함께 넘기세요. 넘기지 않으면 원래 설정의 켜진 dynapath가 그대로 이어져 ValueError가 발생합니다.

import dataclasses

from korail_mobile_api import DynapathConfig, build_config_from_env

config = dataclasses.replace(build_config_from_env(), disable_dynapath=True, dynapath=DynapathConfig())

DynaPath를 끄면 로그인할 수 없습니다

DynaPath를 끄면 login은 서비스 상태 확인과 공통 코드 조회까지 보낸 뒤, 로그인 요청을 보내기 전에 KorailDynaPathRequiredError를 발생시킵니다. 따라서 로그인이 필요한 메서드는 모두 쓸 수 없습니다. 라이브러리가 요청 전에 거절하는 경로는 로그인뿐이지만, 열차 조회처럼 토큰을 붙이던 다른 경로도 서버에서 거절될 수 있습니다.

대기열(NetFunnel)

열차 조회, 예약, 결제, 예약 내역 조회는 요청 전에 대기열을 거칩니다. 대기열은 작업 종류별 관문으로 나뉩니다. 대기열 서버와 주고받는 순서는 요청 흐름의 "대기열 통과" 단계를 참고하세요.

관문 메서드 대기열 서버 오류 시
inquiry search_trains, search_transfer_trains, search_trains_with_transfer_fallback, get_seat_assignment_schedule 경고 로그를 남기고 대기열 없이 요청을 보냅니다.
peak_season_inquiry 위 네 메서드에 peak_season=True를 넘겼을 때 같음
product_inquiry 열차 조회 세 메서드에 use_special_schedule=True를 넘겼을 때(peak_season보다 우선) 같음
reserve reserve, reserve_transfer, reserve_with_discount_card 요청을 보내지 않고 KorailNetFunnelError를 발생시킵니다.
pay pay_with_card 같음
reservation_view get_reservation_history 같음
  • 대기열 서버가 요청을 차단하면(301, 302) 모든 관문에서 KorailQueueRejectedError가 발생합니다.
  • 대기열 서버가 기다리라고(201, 202) 답하면서 입장 키를 주지 않으면 모든 관문에서 요청을 보내지 않고 KorailNetFunnelError를 발생시킵니다.
  • reserve, pay, reservation_view 관문은 대기열 서버가 최종적으로 통과(200)로 답하지 않으면 요청을 보내지 않고 KorailNetFunnelError를 발생시킵니다.
  • 대기열 요청이 실패하면 관문을 통과하는 동안 한 번만 다시 보냅니다. 다시 보내기 전에는 실패한 요청의 제한 시간(netfunnel_timeout)이 다 찰 때까지 기다립니다.
  • 대기열을 건너뛰거나 입장 키 반납이 실패하면 korail_mobile_api.netfunnel 로거에 경고를 남깁니다.

대기 시간 제한

netfunnel_wait_limit를 정하면 관문을 통과하는 데 걸린 누적 시간이 이 값을 넘을 때 API 요청을 보내지 않고 KorailNetFunnelError를 발생시킵니다. 다음 대기를 시작하기 전에 대기 후 시각이 상한을 넘을지 확인하고, 통과한 직후에도 한 번 더 확인합니다. API 요청과 입장 키 반납에 걸린 시간은 포함하지 않습니다.

from korail_mobile_api import KorailClient, KorailConfig, KorailNetFunnelError, TrainSearchQuery

client = KorailClient(KorailConfig(netfunnel_wait_limit=120.0))
try:
    query = TrainSearchQuery(
        departure_station_code="서울",
        arrival_station_code="부산",
        departure_date="20261002",
        departure_time="090000",
        passengers=1,
    )
    result = client.search_trains(query)
except KorailNetFunnelError as error:
    print("대기열을 통과하지 못했습니다:", error.code, error.message)
finally:
    client.close()

관문 식별값

netfunnel_actions는 관문 이름(inquiry, peak_season_inquiry, product_inquiry, reserve, pay, reservation_view)을 키로, 대기열 서버에 보낼 식별값을 값으로 받습니다. 라이브러리의 기본 식별값은 앱 내부 값이 공개돼 있지 않아 확인하지 못한 값입니다. 정확한 값을 알 때만 바꾸세요. 목록에 없는 관문 이름과 빈 문자열 값은 무시합니다.

대기열 끄기

netfunnel_enabled=False이면 대기열 클라이언트를 만들지 않고 모든 요청을 바로 보냅니다.

대기열을 끄는 것은 권장하지 않습니다

앱은 이 요청들을 보내기 전에 대기열을 거칩니다. 대기열을 끄면 앱과 다르게 동작하고 KORAIL의 혼잡 제어를 건너뛰게 됩니다. 기다리는 시간이 문제라면 대기열을 끄는 대신 netfunnel_wait_limit로 상한을 정하세요.

언어 필드(lang)

lang을 정하면 공통 필드를 싣는 요청에서 Key 다음(공통 필드에 Key가 없는 요청은 Version 다음)에 lang을 보냅니다. None이면 보내지 않습니다. 앱이 보내는 값은 앱 내부 값이 공개돼 있지 않아 확인하지 못했으므로, 라이브러리는 기본값을 추측해 보내지 않습니다.

환경변수 설정

build_config_from_env는 환경변수에서 실제 기기의 값을 읽어 KorailConfig를 만듭니다. DynaPath 토큰의 기기값과 대기열 User-Agent에 같은 기기값을 씁니다. 앱 시작 시각은 함수를 호출한 시각입니다. 계정 ID, 비밀번호, 카드 정보 같은 자격 증명은 읽거나 저장하지 않습니다.

환경변수 필수 기본값 쓰이는 곳
KORAIL_DYNAPATH_DEVICE_ID 예 - 토큰의 기기 식별자(device_id)
KORAIL_DYNAPATH_OS_VERSION 예 - 토큰의 os_version, DynapathConfig.os_version, 대기열 User-Agent의 안드로이드 버전
KORAIL_DYNAPATH_DEVICE_MODEL 예 - 토큰의 device_model, DynapathConfig.device_name, 대기열 User-Agent의 기기 모델
KORAIL_ANDROID_BUILD_ID KORAIL_NETFUNNEL_USER_AGENT가 없을 때 - 대기열 User-Agent의 Build/... 부분
KORAIL_NETFUNNEL_USER_AGENT 아니요 위 기기값으로 만든 Dalvik/2.1.0 (...) 문자열 netfunnel_user_agent
KORAIL_DYNAPATH_AS_VALUE 아니요 라이브러리 기본값 토큰의 as_value
KORAIL_ADVERTISING_ID 아니요 "" advertising_id
KORAIL_BASE_URL 아니요 "https://smart.letskorail.com:443" base_url
KORAIL_USER_AGENT 아니요 "korailtalk" user_agent
KORAIL_DEVICE_WIDTH 아니요 1440 device_width
KORAIL_DEVICE_HEIGHT 아니요 3120 device_height
KORAIL_ANDROID_SDK_INT 아니요 37 android_sdk_int

필수 환경변수가 없거나 비어 있으면 RuntimeError가 발생합니다. 정수 필드에 숫자가 아닌 값을 넣으면 ValueError가 발생합니다. 대기열 설정과 lang처럼 표에 없는 필드는 기본값을 씁니다. 바꾸려면 dataclasses.replace를 씁니다.

export KORAIL_DYNAPATH_DEVICE_ID="<기기 식별자>"
export KORAIL_DYNAPATH_OS_VERSION="<안드로이드 버전>"
export KORAIL_DYNAPATH_DEVICE_MODEL="<기기 모델명>"
export KORAIL_ANDROID_BUILD_ID="<빌드 ID>"
import dataclasses

from korail_mobile_api import KorailClient, build_config_from_env

config = dataclasses.replace(build_config_from_env(), netfunnel_wait_limit=120.0)
client = KorailClient(config)
try:
    status = client.get_service_status()
finally:
    client.close()

서버 주소

서버 주소는 검사하지 않습니다

base_url과 netfunnel_url은 검사하지 않고 그대로 씁니다. build_config_from_env도 KORAIL_BASE_URL 환경변수 값을 그대로 씁니다. base_url이 다른 서버를 가리키면 로그인 ID와 비밀번호, 세션 쿠키, 카드 정보까지 모든 API 요청이 그 서버로 갑니다. 비밀번호 암호화 키도 같은 서버에서 받으므로, 그 서버에 대해서는 암호화가 보호가 되지 않습니다. netfunnel_url이 다른 서버를 가리키면 대기열 요청이 그 서버로 가고, 그 서버의 응답에 따라 API 요청을 보낼지와 언제 보낼지가 정해집니다. 신뢰할 수 있는 주소가 아니면 바꾸지 마세요.