에이전트로 KTX 표를 예매하려다 Korail2에 N카드 기능을 붙인 이야기
에이전트로 KTX 표를 예매하려다 Korail2에 N카드 기능을 붙인 이야기
작성일: 2026-05-11
처음에 하고 싶었던 건 단순했다. 에이전트에게 “내일 대전에서 서울 가는 KTX 표를 찾아서 예약해줘”라고 맡기고 싶었다.
그런데 실제로 내가 쓰는 예매 방식은 일반 승차권 예매가 아니었다. 나는 코레일에서 파는 N카드라는 할인 서비스를 이미 구매해 둔 상태였고, 평소에는 코레일톡에서 나의 티켓 > 정기권패스 > N카드 > 승차권 예매로 들어가 할인승차권을 예매하고 있었다.
문제는 기존 자동화가 기대고 있던 korail2에는 이 N카드 할인승차권 흐름이 없었다는 점이다. 그래서 오늘 작업은 “KTX 예매 자동화”에서 시작했지만, 실제로는 korail2에 보유 N카드 기반 조회와 예약 흐름을 추가하는 일이 됐다.
참고한 리포지토리
이번 작업에서 기준이 된 프로젝트는 두 개였다.
korail2원본: https://github.com/carpedm20/korail2korail2fork: https://github.com/ce-dric/korail2k-skill원본: https://github.com/NomaDamas/k-skillk-skillfork: https://github.com/ce-dric/k-skill
korail2는 실제 코레일 로그인, 열차 조회, 예약 API를 다루는 Python 라이브러리이고, k-skill은 그 위에 KTX 예매 자동화 skill을 제공하는 프로젝트다.
그래서 N카드 자동화를 하려면 아래 레이어인 korail2와 위 레이어인 k-skill을 둘 다 봐야 했다.
1. 문제 정의
자동 예매를 만들려면 먼저 기존 도구가 무엇을 할 수 있고, 무엇을 못 하는지 확인해야 했다.
korail2는 코레일 로그인을 하고 일반 KTX/Korail 열차를 조회하거나 예약하는 기능을 제공한다.- 내가 원하는 건 그 위에서 N카드 할인을 적용한 승차권을 찾고 예약하는 것이다.
- 하지만 기존
korail2에는 보유 N카드를 조회하거나, N카드 할인승차권 목록을 조회하는 기능이 없었다. - 따라서 에이전트가 표를 예매하려면 먼저
korail2에 N카드 플로우를 추가해야 했다.
여기서 핵심은 N카드가 승차권 자체가 아니라 할인 수단이라는 점이었다.
즉, 일반 승차권 예약 API에 할인값만 얹는 것으로는 부족하고, KorailTalk 앱의 N카드 전용 흐름을 따로 분석해야 했다.
2. 왜 그냥 일반 예약으로는 안 됐나
처음에는 “일반 예약 요청에 N카드 번호나 할인 코드만 추가하면 되지 않을까?”라고 생각할 수 있다.
하지만 실제 코레일톡 앱을 보면 N카드 할인승차권은 별도 화면에서 출발한다.
- 먼저 사용자는 N카드를 구매한다.
- 구매한 N카드는
나의 티켓 > 정기권패스에 나타난다. - 그 N카드 안에서
승차권 예매를 눌러야 할인승차권 조회 화면으로 간다. - 그 화면에서 방향과 날짜를 조정한 뒤
열차 조회하기를 누르면 할인율, 매진, 입석, 예약 가능 여부가 나온다.
사용자 화면에서 확인한 정보도 중요했다.
- 카드 상태:
6회 / 10회 - 유효기간:
2026년 4월 11일 ~ 2026년 7월 9일 - 예매 화면에서 출발/도착 방향과 날짜를 바꿀 수 있음
열차 조회하기를 누르면 할인율, 매진, 입석, 예약 가능 여부가 함께 보임
이 흐름을 기준으로 KorailTalk APK를 분석해 보니, N카드에는 크게 두 흐름이 있었다.
N카드 상품 조회/구매흐름이미 구매한 N카드로 할인승차권을 예매하는 흐름
처음에 눈에 띈 dcntCrdScheduleView.do는 N카드 상품/종류 기반 조회에 가까웠다.
반면 실제로 필요한 건 보유 N카드의 승차권 예매 화면에서 호출되는 assignScheduleView.do 흐름이었다.
3. 구현한 것
그래서 korail2에 보유 N카드 기준 기능을 추가했다.
추가한 기능
owned_ncards()- 현재 계정의 보유 N카드를
MyTicketList/SelTicketInfo기반으로 읽어온다.
- 현재 계정의 보유 N카드를
search_owned_ncard_trains()- 보유 N카드를 넣으면 KorailTalk의 N카드 예매 화면과 같은 열차 조회를 한다.
- 조회 결과에서
15%할인,예약하기,입석, 잔여석 같은 상태를 읽을 수 있다.
NCard/NCardTrain확장- N카드의 구간, 유효성, 회차, 할인 정보, 잔여 좌석 정보를 더 자세히 다룰 수 있게 했다.
- README 갱신
- N카드 흐름은 “상품 조회”보다 “보유 카드 기준 조회”를 먼저 쓰도록 설명을 고쳤다.
구현 방향
- 자동 결제는 넣지 않았다.
- 예약 제출은 결제 직전까지 확인만 했다.
- 사용자에게 필요한 건 “어떤 열차를 고를 수 있는지”와 “그 예약이 어떤 좌석으로 잡히는지”였기 때문에, 그 경계까지만 구현했다.
- 에이전트가 무조건 결제까지 밀어붙이는 건 위험하다고 보고, 결제는 사용자가 직접 마무리하는 구조를 유지했다.
4. 실제 검증 결과
실제로 계정 세션을 써서 조회를 재현했다.
- 날짜:
2026-05-12 - 구간:
대전 -> 서울 - 시간대:
10:00 ~ 12:00 - 대상:
KTX
조회 결과:
- KTX 후보가 정상적으로 나왔다.
15%할인라벨이 응답에 들어왔다.022편은10:58 -> 11:53으로 확인됐다.
그 다음 실제 예약도 해 봤다.
- 대상 열차:
022 - 좌석 조건: 8번, 9번은 피하기
- 선호:
A/D창가석 - 결과:
13호차 10A로 예약이 잡혔다.
즉, 요청한 조건을 만족하는 결제 전 예약 상태까지는 실제로 재현됐다.
5. 얻은 결론
이번에 확인한 핵심 결론은 네 가지였다.
- 에이전트가 KTX를 예약하게 하려면, 기반 라이브러리가 내가 실제로 쓰는 예매 방식을 지원해야 한다.
N카드 할인승차권은 일반 예매와 다른 진입점이 있다.- 보유 N카드 기준으로 조회해야 앱과 같은 할인승차권 목록을 얻을 수 있다.
- 예약은 가능하지만, 결제 자동화는 별도 검증 없이 넣지 않는 편이 맞다.
6. 작업하다가 k-skill을 발견했다
이 작업을 하던 중 LinkedIn에서 k-skill이라는 프로젝트를 발견했다.
k-skill은 여러 자동화 skill을 모아 둔 프로젝트였고, 그 안에 ktx-booking이라는 기능이 있었다. 문서를 보니 KTX/Korail 열차 조회, 좌석 확인, 예약 진행, 예약 내역 확인, 예약 취소 같은 기능을 제공하고 있었다.
그런데 내부적으로는 결국 korail2를 기반으로 코레일 조회와 예약을 처리하는 구조였다. 즉, 내가 처음에 부딪힌 문제와 연결된다.
k-skill은 KTX 예매 자동화를 제공한다.- 그 자동화는
korail2를 기반으로 한다. - 하지만
korail2에는 N카드 할인승차권 흐름이 없었다. - 따라서
k-skill에서 N카드 예매를 제대로 지원하려면, 먼저korail2쪽에 기능이 있어야 했다.
그래서 작업 방향이 더 분명해졌다. 단순히 내 로컬에서 동작하는 스크립트를 만드는 대신, 먼저 korail2 fork에 N카드 기능을 정리하고 PR로 남기는 쪽이 맞았다.
이후에는 같은 흐름을 k-skill에도 연결했다. k-skill의 ktx-booking skill이 N카드 할인승차권을 다룰 수 있도록 fork를 받아 작업했고, 원본 프로젝트에도 PR을 올렸다.
결과적으로 작업은 두 단계가 됐다.
korail2에 N카드 기반 조회/예약에 필요한 저수준 기능을 추가한다.k-skill의ktx-booking에서 그 기능을 사용자-facing 자동화로 노출한다.
7. PyPI에 배포했다
k-skill에서 N카드 기능을 쓰려면 korail2에 N카드 코드가 있어야 했다. 그런데 원본 korail2에는 없으니, 내 fork를 PyPI에 올리는 게 맞다는 결론이 됐다.
패키지 이름은 korail2-ncard로 정했다. korail2와 구분되면서도 N카드 지원 fork임을 바로 알 수 있는 이름이었다.
pip install korail2-ncard pycryptodome
빌드는 pyproject.toml + setuptools + twine으로 진행했다. 시스템 Python이 PEP 668로 외부 패키지 설치를 막아서 venv를 따로 만들어서 빌드했다. 배포 후 pip install korail2-ncard가 실제로 되는지, README에 있는 예시가 동작하는지 확인했다.
PyPI 페이지: https://pypi.org/project/korail2-ncard/
8. 커밋과 PR
PR은 두 곳에 나누어 정리했다.
korail2
- PR: https://github.com/ce-dric/korail2/pull/1
- 원본 리포지토리: https://github.com/carpedm20/korail2
- fork 리포지토리: https://github.com/ce-dric/korail2
- 브랜치:
ncard-discount-poc - 주요 변경 파일:
korail2/korail2.py,README.md - 목적:
k-skill같은 상위 자동화 도구가 N카드 할인승차권 플로우를 재사용할 수 있게 기반 기능을 추가하는 것
이 PR에는 다음 내용을 담았다.
- KorailTalk의 현재 모바일 API 인증 흐름 대응
- 보유 N카드 조회
- 보유 N카드 기준 할인승차권 열차 조회
- 위험한 예약/취소 테스트는 명시적으로 켜야만 실행되도록 제한
- 결제 자동화는 범위 밖으로 유지
k-skill
- PR: https://github.com/NomaDamas/k-skill/pull/231
- 원본 리포지토리: https://github.com/NomaDamas/k-skill
- fork 리포지토리: https://github.com/ce-dric/k-skill
- 브랜치:
feat/ncard-v2 - 제목:
feat: N카드 할인 예매 지원 추가 (ktx-booking)
이 PR은 korail2에 추가한 N카드 흐름을 k-skill의 ktx-booking 기능에서 사용할 수 있게 연결하는 쪽이다.
즉, korail2 PR이 기반 라이브러리 작업이라면, k-skill PR은 에이전트가 실제로 호출할 수 있는 자동화 인터페이스 쪽 작업이다.
9. 짧은 회고
이번 작업은 “에이전트로 KTX 표를 예매하고 싶다”에서 시작했다.
그런데 막상 들여다보니 내가 원하는 예매 방식은 일반 승차권 예매가 아니라 N카드 할인승차권 예매였다. 그리고 그 기능은 korail2에 없었다. 그래서 먼저 기반 라이브러리에 없는 플로우를 분석하고 추가해야 했다.
중간에 k-skill을 발견하면서 이 작업의 위치도 더 명확해졌다. 에이전트가 KTX 표를 예매하는 최종 사용자 경험은 k-skill 같은 상위 자동화에서 만들 수 있지만, 실제 코레일 API를 다루는 기반은 korail2다. 그래서 N카드 기능은 아래쪽 라이브러리에 먼저 넣고, 그 다음 k-skill에서 자동화 명령으로 연결하는 흐름이 자연스러웠다.
이번 작업에서 제일 중요했던 건 “어떤 endpoint를 써야 하는지”를 잘못 짚지 않는 것이었다. 처음에는 N카드 상품 조회 endpoint를 보게 되는데, 실제 사용자가 앱에서 누르는 흐름은 보유 카드 -> 승차권 예매 -> 열차 조회였다.
그 차이를 잡아낸 뒤에는 응답 필드 해석과 예약 결과 검증이 훨씬 선명해졌다.
즉, 이건 단순한 자동 예약 스크립트 추가가 아니라, 내가 실제로 쓰는 코레일 할인 예매 방식을 에이전트가 다룰 수 있도록 기반 라이브러리와 상위 자동화 skill 양쪽에 기능을 채워 넣고, 그 결과를 오픈소스 PR로 정리한 작업이었다.