콘텐츠로 이동

7.0.10 정적 검토와 변경 범위

2026-10-19 코레일+ 안드로이드 앱 7.0.10과 SDK를 정적으로 대조한 결과의 공개 요약입니다. 공개 메서드 85개를 각각 한 행씩 검토했지만, 보호된 값·전체 분기·최종 wire 요청을 모두 확인한 것은 아닙니다. 앱 실행이나 실서버 요청, 실제 예약·결제·환불은 하지 않았습니다. 이 검토는 7.0.10 실서버 호환 인증이 아닙니다.

버전 구분

이 검토의 변경 사항은 이후 패키지 2.6.0에 포함합니다. 아래 버전·배포 상태는 정적 검토 당시의 기록이며, 당시의 관측을 2.6.0 배포본 자체의 실서버 검증으로 바꾸지 않습니다.

구분 정적 검토 당시 개발 변경
오류 메시지 사전 앱 7.0.10 기준, 일반 결과 코드 10,305개·메시지 17,256개
요청 기본 AppVersion 7.0.8 유지. KorailConfig(app_version="7.0.10") 명시 가능
API Version 기존 값 유지. 앱 버전과 별개
패키지 버전 2.5.0 유지. 변경 이력은 Unreleased
태그·배포 이 작업에서 생성·실행하지 않음

사전 차이는 WRD000058 한국어 안내의 1개월→2개월입니다. 영어는 그대로 보존합니다. 메시지를 근거로 날짜 제한·오류 분류를 변경하거나, 사전 버전을 요청 버전으로 간주하지 않습니다.

반영하는 계약

요청 순서

  • 은행 조회의 직접 @Field 경로는 Device, Version, Key, AppVersion 순서입니다. 빈 Key, AppVersion=None 생략, lang 미전송 규칙은 유지합니다.
  • 환불과 환불 수수료 조회는 상위 공통 필드를 먼저 직렬화합니다. 생성자의 인자 나열 순서와 직렬화 순서를 구별합니다.
  • 이는 확인한 앱 근거와의 정합성 수정입니다. 보호된 최종 map·서버의 순서 민감성·실서버 영향은 미확인입니다.

환불 대상 연결 — fail-closed 변경

get_refund_commission이 반환하는 응답에 조회 당시의 원표 식별자·동반자 정보를 비공개 문맥으로 연결합니다. refund는 성공 응답인지 확인하고 다음 값이 환불 입력과 일치해야 요청합니다.

  • 원표 창구번호, 원표 반환일, 일련번호, 반환 비밀번호
  • 동반자 이름, 증빙번호(상세의 동반자 생년월일)

직접 생성·직접 파싱한 수수료 응답, 다른 승차권의 응답, 원표 반환일이 없는 PaidTicket은 요청 전에 거절합니다. 성공 봉투만 수동으로 채우던 호출 방식은 더 이상 허용되지 않습니다. 수수료 자동 조회나 환불 자동 재시도는 없습니다.

PaidTicket의 새 키워드 전용 필드는 return_sale_date: str | None = None, companion_name: str = "", companion_certificate_no: str = ""입니다. from_refund_detail()은 상세의 original_sale_date를 원표 반환일로, 동반자 이름·생년월일을 동반자 필드로 복사합니다. 현재 판매일인 sale_date는 별도로 유지합니다. 수동 생성 시 원표 반환일을 명시해야 합니다. 기존 get_refund_commission 시그니처는 그대로이며, 간편 API는 TicketResource.refund_fee(ticket, *, companion=RefundCompanion())로 동반자 정보를 받습니다.

조회 문맥과 새 필드는 repr에서 숨기고 개인정보를 파일로 저장하지 않습니다. 직접 속성 접근·raw·일반 dataclass 직렬화 전체를 마스킹하는 기능은 아닙니다. 이 연결은 대상 혼용을 막는 로컬 검증일 뿐, 수수료 금액의 유효기간·서버 승인·임의 변조 방지를 보장하지 않습니다. 조회 입력에 없는 PNR·현재 판매일·재발행 세대까지 연결하는 검사는 아닙니다. 실제 사용 순서는 환불 가이드를 참고하세요.

좌석배정 조회의 기존 확장

seatAttCdN/psgNumN/stlbDturDvNmN의 *2 이상 번호 그룹은 앱 DTO에서 확인한 단일 묶음과 다른 SDK 확장입니다. 기존 1~9명 입력과 폼 생성은 유지하며, 여러 명을 한 명으로 무음 축소하지 않습니다. 과거 열차 목록·거절 코드 관측은 보존하지만 그것으로 모든 번호 그룹의 서버 의미를 확정하지 않습니다.

미확정 — 임의로 바꾸지 않는 부분

항목 남은 확인 사항과 정책
DynaPath 조건부(gated) 문자열·nullable code와 응답 gate의 정확한 조건이 미확정입니다. 기존 정수 heuristic을 새 앱과 동등하다고 주장하지 않으며 토큰·검사를 끄거나 규칙을 추측하지 않습니다.
통합결제 통합결제와 기존 결제 선언이 공존합니다. 일반 회원·단일 PNR·국내카드 분기를 확인하기 전 endpoint 교체·교차 재시도를 하지 않습니다.
서비스 상태 CommonIn 상속만으로 기본값이 실제 전송된다고 단정하지 않습니다. encodeDefaults와 emitted map이 미확정이므로 필드를 임의 추가하지 않습니다.
N카드·보호 기본값 성공 분기·직렬화 근거가 부족한 부분을 호환 완료로 바꾸거나 값을 추측하지 않습니다.

Android push ACK, 비회원 UI 경고, 열차 선택 안내, 진단 화면은 회원용 Python SDK에 이식하지 않습니다. 0원·예약대기(STANDBY) 홀드 카드 결제 차단, 보수적 금액 검증, 원문 보존, 변경 요청 재전송 금지는 유지합니다.

기존 실서버 기록과의 관계

7.0.8 실서버 검증, 2.5.0 출시 전 검증, 실서버 확인 현황은 당시 설정·날짜·관측값의 기록으로 그대로 보존합니다. 새 정적 검토를 그 집계에 합산하거나, 과거 결제·환불 성공을 이번 변경의 실서버 성공으로 표시하지 않습니다. 오프라인 검사의 통과도 서버 수용이나 모든 응답 분기의 호환을 보장하지 않습니다.

변경 후 오프라인 확인

2026-10-19 로컬 Python 3.14.7에서 전체 테스트 2,507개, 분기 포함 전체 커버리지 93.21%를 확인했습니다. Ruff 검사·포맷, mypy strict, Pyright, 엄격 문서 빌드, wheel/sdist 빌드와 Twine 검사도 통과했습니다. 별도 임시 경로에 로컬 wheel을 설치해 새 사전·요청 순서와 환불 대상 불일치·원표 날짜 누락·미연결 응답의 전송 전 거절을 확인했습니다. 이 검사에서도 소켓을 차단하고 MockTransport만 사용했습니다. 시스템 설치본은 교체하지 않았습니다.

tests/test_7010_contract.py는 앱의 직접 Field 선언과 상위 serializer 호출 순서에서 가져온 작은 독립 계약을 고정합니다. APK·소스 SHA-256과 관측 범위를 함께 적고, 앱 원문·보호 데이터·자격증명은 포함하지 않습니다. 기존 SDK 자체에서 캡처한 golden만으로 앱 wire 동등성을 주장하지 않으며, 환불 전체 흐름·불일치·원문 보존·재전송 금지는 tests/test_refund_binding.py에서 별도로 검사합니다.

요청 AppVersion 7.0.10 실서버 확인

위 정적 검토 이후인 2026-10-19, 작업 브랜치의 소스를 app_version="7.0.10"으로 명시해 별도 확인했습니다. 배포된 패키지나 실제 안드로이드 앱 실행을 검증한 것은 아닙니다. 기기값은 SDK 기본 합성 프로파일을 사용했습니다.

범위 관측 결과
공개 메서드 85개 중 64개 실행. 로컬 전용 close·clear_session 2개 포함
반환 56개는 FAIL 없이 반환(빈 결과 포함), 1개는 FAIL 모델, 7개는 서버 거절·자료 없음 예외
전송 HTTP 120건 모두 200. HTTP 성공만으로 기능 성공을 집계하지 않음
임시 예약 직통·환승 각 1건 생성 후 2건 모두 취소. 원래 예약과 최종 예약은 모두 0건
홀드 복원 각 테스트 홀드의 PNR·정산액 일치 확인. 실제 결제 요청은 보내지 않음
정리 두 인증 배치 모두 로그아웃·연결 종료. 미정리 테스트 홀드 없음

다자녀 조건 미충족, 조회 자료 없음, 좌석 변경 대상 부재 등 서버의 업무상 거절은 정상 기능 성공으로 간주하지 않습니다. 과거 승차권의 수수료 조회는 WRT200022로 거절되어 유효한 수수료 응답과 환불 대상 연결의 정상 실서버 흐름은 미검증입니다. 실제 환불은 요청하지 않았습니다. 대상 연결의 일치·불일치 검사는 위 오프라인 회귀 결과와 구별합니다.

나머지 21개는 결제·환불·기존 표 변경 등 허용 범위 밖이거나 N카드·공항버스·실물 좌석 QR 등의 전제 입력이 없어 실행하지 않았습니다. 변경 요청 재전송이나 추가 테스트 홀드 생성으로 성공을 강제하지 않았으며, 계정 정보·쿠키·토큰·PNR·승차권 원문은 공개하지 않습니다. 이 결과는 명시한 요청 버전의 제한된 서버 수용 근거이며, 보호된 앱 기본값·DynaPath 차단 분기·전체 응답 계약의 검증은 아닙니다. 요청 기본 버전·패키지 버전·과거 검증 집계는 변경하지 않고 릴리즈·배포도 진행하지 않습니다.

2026-10-23 기본 2! 진단

Unreleased의 기본 클라이언트와 profile/env 설정을 SDK 2.0.1 native-unavailable 진단으로 전환합니다. 요청 버전·전송/오류 정책·대기열·공개 시그니처는 main의 기존 동작을 유지합니다. 기본 헤더는 다음과 같고, 기본 토큰에 기기값을 합성하지 않습니다.

2!v=2&es=165489088780120535&st=Android&sv=2.0.1&ai=korailplus

정적 근거는 7.0.10 APK SHA-256 2847ceb54ba7f7e26edf3426ed606bb1f7e7dfabacf01011d6d164eb45dc993b의 J0.java:233–273/J0.smali:1917에 있는 2! 직렬화와 J0.java:303–307의 native-unavailable 코드입니다. profile/env로 제공한 OS·모델·기기 식별자는 UTF-8의 padding 없는 Base64url로 os·dm·iid 순서에 넣습니다. 명시적 v1 token_settings·외부 공급자와 기존 경로 allowlist·명시적 헤더 생략·필수 토큰·무재시도 계약은 유지합니다. 기본 설정의 pickle 복원도 검사합니다. env builder는 v1 전용 KORAIL_DYNAPATH_AS_VALUE를 더 이상 읽지 않습니다.

기존 실서버 관측

2026-10-23 01:40~01:47 KST, 정리 전 작업트리(23799f9 기반)의 같은 기본 헤더 형식으로 확인한 결과입니다. macOS·Python 3.14.7·HTTPX 0.28.1, AppVersion=7.0.10·API Version=250722001, 프록시 없음·TLS 검증·대기열 사용 조건입니다. 이 축소판을 실서버에서 다시 실행한 기록은 아닙니다. 이번 변경에서 요청 기본 버전은 기존 main 값을 유지하므로 관측 조건과 구별합니다.

범위 실제 관측
기본 헤더 allowlist에 따른 15개 요청에 위 2! 헤더 부착. 기기값 합성·공급자 override 없음
로그인·조회 Login POST 3회 성공, 익명/인증 열차·좌석·운임 등 주요 조회 반환
미결제 예약 10월 30일 서울→부산 성인 1명, 직통·환승·좌석 지정 각 1건 생성 IRR000018
상세·복원·취소 세 홀드 모두 상세 IRZ000001, 복원한 PNR·금액 일치, 취소 IRG000000
정리 시험 홀드 3건 모두 취소, 예약 전후 0건·장바구니 원복·로그아웃·연결 종료
전체 범위 HTTP 182건 모두 200. 공개 메서드 65개 중 FAIL 모델 1개·서버 거절/자료 없음 예외 8개 포함, HTTP 성공과 기능 성공을 구별

자격증명·쿠키·토큰값·회원/예약/승차권 식별자·원문 응답은 공개하지 않습니다. 결제·발권·실제 환불·기존 표 변경은 실행하지 않았습니다. PyPI 배포본·모든 서비스·다른 요청 버전·서버의 장기 허용 정책을 보증하지 않습니다. 이번 코드 검증은 합성 응답·소켓 차단으로 헤더·경로 gate·인증 bootstrap·설정·v1 보존을 확인합니다.