시스템이 당신을 거부할 때: 우즈베키스탄 열차 예매 시차 버그 관찰기
5 min read
시스템이 당신을 거부할 때: 우즈베키스탄 열차 예매 시차 버그 관찰기
여행 경험

어떤 시스템은 특정한 사람들만을 위해 설계됩니다. 그들에게는 모든 것이 매끄럽게 작동하지만, 다른 이들에게 그 시스템은 손잡이 없는 문과 같습니다. 들어갈 수 있다는 것은 알지만, 아무리 밀어도 반응이 없습니다.

우즈베키스탄 여행을 계획하던 중, 저는 바로 그런 문을 마주했습니다.

목적지는 타슈켄트에서 출발하는 부하라(Bukhara)였습니다. 우즈베키스탄 철도청 공식 예매 사이트 eticket.railway.uz를 찾았습니다. 계정을 만들고, 열차 편(06:10 출발, 10:16 도착)을 고르고, 객차와 창가 좌석까지 선택했습니다. 하지만 ‘확인’ 버튼을 누를 때마다 시스템은 주문을 취소하고 저를 메인 화면으로 돌려보냈습니다.

오류 메시지는 없었습니다. 마치 아무 일도 없었다는 듯, 정중하고 깔끔한 리다이렉트뿐이었습니다.

여행사의 견적은 거울이다

먼저 여행사에 물었습니다. 타슈켄트에서 부하라까지의 티켓 세 장에 140유로.

그 숫자를 보며 잠시 생각에 잠겼습니다. 비싸고 싸고의 문제가 아니었습니다. 만약 제가 이 조건을 그대로 받아들인다면, 저는 이 티켓의 ‘진짜 가격’이 얼마인지 영원히 알 수 없게 됩니다. 여행사의 서비스는 가치가 있고 중개인이 필요한 순간도 분명 있지만, 이번만큼은 제가 정말로 스스로 해결할 수 없는 일인지 확인하고 싶었습니다.

사용할 수 없는 것과 시도하지 않는 것은 전혀 다른 문제입니다.

시스템은 스스로 주문을 취소했다

페이지가 넘어가지 않는 것은 기술적인 문제였습니다. 하지만 ‘해결할 수 없는’ 종류는 아니었습니다. 브라우저의 개발자 도구(DevTools → Network)를 열고 예매 과정을 다시 밟으며, 모든 API 요청을 지켜보았습니다.

주문이 생성되었습니다. 시스템은 orderId를 발급받았습니다. 그런데 메인 화면으로 튕겨 나가는 그 찰나, 저는 하나의 POST 요청이 다음 주소로 날아가는 것을 보았습니다.

/api/v1/universal-orders/cancel/{orderId}

제가 취소한 것이 아닙니다. 시스템이 스스로 취소한 것입니다.

이어서 주문 만료 시간을 알려주는 API를 확인했습니다.

GET /api/v1/universal-orders/process/end-time/{orderId}

결과는 다음과 같았습니다.

{
  "response": {
    "endLifeTime": "2026-04-12T13:04:31.226000615"
  }
}

겉보기에는 정상입니다. 하지만 이 시간 문자열에는 한 가지가 빠져 있었습니다.

사라진 기호 하나

"2026-04-12T13:04:31.226000615"

이 시간 문자열에는 시차(Timezone) 정보가 없었습니다. Z도, +05:00도, 아무것도 없었죠.

JavaScript에서 new Date("2026-04-12T13:04:31")의 동작은 브라우저가 실행되는 환경의 시차를 따릅니다. 우즈베키스탄 현지인(UTC+5)에게 이 시간은 현지 시간 13:04로 해석되어 정확히 작동합니다. 하지만 한국이나 대만 같은 UTC+8 지역의 사용자가 접속하면, 브라우저는 이를 현지 시간 13:04로 해석합니다. 이를 다시 UTC로 환산하면 05:04가 되어, 우즈베키스탄 현지 시간보다 정확히 3시간이나 앞서게 됩니다.

시스템은 결제를 위해 15분의 시간을 줍니다. 하지만 브라우저가 페이지를 여는 순간, 이미 3시간 전에 만료된 주문이라고 판단해 버리는 것입니다.

타이머는 즉시 0이 되고, 주문은 취소되며, 사용자는 다시 첫 화면으로 쫓겨납니다.

복잡한 버그는 아닙니다. 하지만 이 작은 실수 하나가 다른 시차에 사는 모든 사람을 정교하게 밀어내고 있었습니다. 시스템이 당신을 거부해서가 아니라, 시스템이 애초에 당신의 존재를 상상하지 못했기 때문입니다.

브라우저 안에서의 수술

문제를 알게 되니 해결책은 간단했습니다.

API 응답이 돌아오는 순간을 가로채서 누락된 시차 정보를 채워주면 됩니다. 우즈베키스탄은 UTC+5(타슈켄트 시간)를 사용하므로, 문자열 끝에 +05:00만 붙여주면 브라우저는 시간을 정확히 해석합니다.

방법은 다음과 같습니다. 예매 페이지에 접속하기 에 개발자 도구의 Console에 아래 코드를 붙여넣고 실행합니다.

const _JSONparse = JSON.parse;
JSON.parse = function(text) {
    const result = _JSONparse.call(this, text);
    if (
        result?.response?.endLifeTime &&
        typeof result.response.endLifeTime === 'string'
    ) {
        const str = result.response.endLifeTime;
        if (
            /^\d{4}-\d{2}-\d{2}T/.test(str) &&
            !str.endsWith('Z') &&
            !/[+\-]\d{2}:\d{2}$/.test(str)
        ) {
            result.response.endLifeTime = str + '+05:00';
            console.log('✓ endLifeTime 수정됨:', result.response.endLifeTime);
        }
    }
    return result;
};
console.log('✓ 시차 수정 코드가 활성화되었습니다. 이제 예매를 진행하세요.');

이 코드는 브라우저의 JSON.parse 함수를 일시적으로 가로챕니다. API 응답이 파싱될 때마다 endLifeTime 필드를 확인하고 시차 정보가 없다면 +05:00을 붙여줍니다. 다른 모든 데이터는 그대로 유지됩니다.

실행 후 다시 예매를 진행해 봅니다. 열차와 좌석을 고르고 확인을 누르자, 이번에는 타이머가 15분부터 정상적으로 줄어들기 시작했습니다. 주문은 메인 화면으로 튕기지 않고 확인 페이지에 그대로 머물렀습니다.

승객 정보와 좌석이 표시된 주문 확인 페이지, 타이머가 정상적으로 작동하고 있다.

클릭하지만 어디로 갈지 모른다

결제 단계에서 한 가지 더 설명할 점이 있습니다.

결제 페이지에 진입하면 몇 가지 결제 수단이 나타납니다. 그중 VISA 아이콘이 있는 옵션은 이론적으로 Stripe 결제창으로 연결되어야 합니다. 국제 신용카드를 사용하는 여행자에게 가장 직접적인 경로죠.

하지만 실제로는 동작이 불안정합니다. 어떨 때는 Stripe로 잘 넘어가지만, 어떨 때는 ‘OCTO Bank’라는 다른 시스템으로 연결되기도 합니다.

시차 수정 코드가 작동 중인 상태의 결제 페이지

OCTO Bank는 우즈베키스탄 현지 은행입니다. 인터페이스에 번체 한국어가 섞여 나오기도 하지만, 디자인을 보건대 중국의 결제 채널을 지원하는 방식에 가깝습니다. 일반적인 국제 여행객에게는 이 경로가 막힐 가능성이 큽니다.

OCTO Bank 결제 완료 화면, 성공 메시지 아래에 주문 번호가 적혀 있다.

만약 OCTO Bank로 연결되었다면 취소하고 다시 시도하는 것을 권장합니다. 몇 번 반복하다 보면 정상적인 Stripe 경로로 진입할 수 있습니다. 왜 이런 일이 일어나는지는 확실치 않지만, 아마도 프런트엔드의 라우팅 문제나 브라우저 상태와 관련이 있을 것입니다. Stripe가 아닌 다른 창이 떴다면 정보를 입력하지 말고 다시 시도하세요.

티켓 세 장, 11만 원

결과적으로 타슈켄트에서 부하라로 가는 티켓 세 장의 가격은 우리 돈으로 약 11만 원 정도였습니다.

여행사가 제시한 견적은 140유로로, 실제 가격의 두 배에 달했습니다.

제가 얼마나 아꼈는지를 계산하려던 것이 아닙니다. 제가 주목한 것은 이것입니다. 만약 제가 이 문제를 파고들지 않았다면, 그 차액은 조용히 사라졌을 것이고 저는 그 사실조차 몰랐을 것입니다. 여행사의 서비스가 무가치하다는 말이 아니라, 이번 경우에는 그 가치가 ‘충분히 해결할 수 있는 문제’를 우회하는 데 쓰였다는 점이 중요합니다. 그리고 그 문제는 고작 몇 글자의 시차 표시가 빠진 문자열 하나였습니다.

때때로 시스템의 ‘불친절함’은 악의적인 배제가 아니라, 무심한 망각에서 비롯됩니다.

세상에 다른 시차를 가진 사람이 있다는 사실을 잊고, UTC+8이 세계의 표준이 아니라는 사실을 잊고, 날짜 문자열에 기호가 빠졌을 때 어떤 일이 벌어질지를 잊는 것입니다.

이런 망각은 고쳐질 수 있습니다. 반드시 공식적인 해결책을 기다릴 필요는 없습니다. 수십 줄의 코드, 개발자 도구의 콘솔 창, 그리고 쉽게 포기하지 않는 약간의 인내심만 있다면 말이죠.