Double Engine Failure: Back to the Hangar After Two Data Providers Changed Course
두 엔진이 모두 멈췄습니다 — 데이터 제공자 두 곳의 정책 변경으로 정비고에 들어간 앱
항공기 추적 앱의 지도 제공자 CARTO와 비행 데이터 제공자 OpenSky Network가 각각 정책을 바꾸면서 서비스가 멈췄습니다. 작성자는 지도 API 키를 적용하고, 오진을 부르는 오류 메시지와 TCP 연결 시간 초과를 추적한 뒤, 제공자를 어댑터로 분리하고 명확한 점검 화면을 띄웠습니다.
- 주제
AI 요약
해커톤에서 우승한 항공기 추적 앱 Metal Birds Watch가 지도와 비행 데이터 제공자의 정책 변경으로 잇따라 멈췄습니다. 작성자는 파티에서 앱을 보여주려다 문제를 발견했고, 두 장애를 하나씩 추적하며 복구 과정과 설계 교훈을 정리합니다.
CARTO 지도 타일에는 API 키가 필요해졌습니다
CARTO는 2026년 8월 28일 무렵 무료 베이스맵 정책을 바꿨습니다. API 키 없이 타일을 요청하면 지도 위에 ‘API KEY REQUIRED’ 문구가 표시됐습니다. 작성자는 무료 키를 발급받아 CSS 사용자 정의 속성에 저장한 타일 URL에 추가했습니다. 지도 코드가 시작할 때와 테마를 바꿀 때 CSS에서 URL을 읽는 구조였기 때문에 JavaScript는 수정하지 않았습니다.
키를 공개 저장소에 올려도 되는지도 검토했습니다. 브라우저가 모든 타일 요청에 키를 포함하므로 DevTools에서 누구나 볼 수 있습니다. 따라서 키 자체를 비밀로 유지하기보다 CARTO 대시보드에서 운영 도메인만 허용하도록 제한했습니다. 로컬 개발 환경에서는 운영용 키가 동작하지 않으므로, 브라우저의 localStorage에 보관한 개인 개발 키를 쓰거나 키를 빼는 보조 함수를 추가했습니다. 개발 키를 파일에 넣지 않아 실수로 저장소에 올릴 가능성도 줄였습니다.
CSP 설정도 확인했습니다. CARTO 문서에 나온 기본 도메인과 기존 정책의 https://*.basemaps.cartocdn.com은 일치하지 않습니다. 와일드카드가 최상위 도메인인 basemaps.cartocdn.com을 포함하지 않기 때문입니다. 작성자는 {s}. 형식의 서브도메인 URL을 유지해 CSP가 타일 요청을 차단하는 일을 피했습니다.
인증 오류가 아니라 TCP 연결 시간 초과였습니다
지도는 복구했지만, 배포 뒤 비행기 데이터 요청이 503 오류를 내고 앱이 비었습니다. 백엔드 로그에는 OpenSky 인증 실패라고 나와 작성자는 OAuth 자격 증명을 두 차례 교체했습니다. 하지만 문제가 계속되자 토큰 발급 코드의 예외 처리를 살폈습니다. 네트워크 시간 초과, DNS 오류, HTTP 오류, 실제 인증 실패를 모두 같은 ‘인증 실패’ 메시지로 바꾸고 있었습니다.
작성자는 원래 예외의 이름과 코드, 원인(cause) 등 세부 정보를 로그에 남겼습니다. 그제야 서버가 자격 증명을 보내기 전, OpenSky 인증 서버와 TCP 연결을 맺지 못한다는 사실을 확인했습니다. 먼저 요청 제한 시간을 30초로 늘렸지만 오류는 계속 10초 만에 발생했습니다. Node.js의 fetch가 사용하는 undici에는 TCP 연결 단계에 별도의 10초 제한이 있었고, 요청에 설정한 제한 시간보다 먼저 연결을 포기했습니다. 작성자는 undici Agent의 connect.timeout도 30초로 설정했습니다.
그래도 연결은 성립하지 않았습니다. 연결 거부는 서버가 응답해 해당 포트에서 듣는 프로세스가 없다고 알려주는 상황입니다. 반면 시간 초과는 응답 자체가 없는 경우입니다. 작성자는 OpenSky에 직접 문의했고, OpenSky가 클라우드 제공자 IP 대역의 요청을 차단하며 특정 IP를 허용 목록에 추가할 수 없다는 답을 받았습니다. 클라우드에서 사용할 유료 옵션은 검토 중이지만, 당시 호스팅 백엔드에서 쓸 수 있는 지원 방법은 없었습니다.
복구가 늦어지는 동안에는 즉시 점검 상태를 알립니다
작성자는 새 데이터 제공자를 찾는 동안 앱을 점검 모드로 전환했습니다. 처음에는 요청이 실패한 뒤 배너를 보여주는 방식을 생각했지만, 백엔드 응답까지 약 10초가 걸렸습니다. 사용자가 빈 지도를 보며 기다리는 사이 앱이 고장 났거나 주변에 비행기가 없다고 오해할 수 있습니다. 그래서 요청 실패를 기다리지 않고 페이지를 열자마자 점검 안내를 보여줍니다.
점검 설정 하나로 지도 로딩, 위치 권한 요청, 주기적 데이터 조회를 모두 멈춥니다. 안내 화면에는 항공 고시(NOTAM) 형식의 공지, 무전 문구, 점검 시작 날짜와 경과 일수, 자주 묻는 질문이 들어갑니다. 질문과 답변은 AI가 아니라 미리 작성한 목록으로 구성했습니다. 로그북 데이터는 서버가 아니라 사용자 브라우저에 저장되므로 안전하다는 안내도 포함했습니다.
비행 데이터 제공자 코드는 이미 어댑터로 분리돼 있었습니다. OpenSky 응답을 앱 내부 형식으로 바꾸는 부분만 제공자별 서비스 파일에서 처리하므로, 새 제공자를 붙여도 경로와 캐시, 프런트엔드를 바꾸지 않아도 됩니다. 교훈은 명확합니다. 실패 원인을 숨기는 일반적인 오류 메시지는 디버깅을 늦춥니다. 연결 거부와 시간 초과는 서로 다른 상황을 가리키므로 구분해 관찰해야 합니다. 무료 외부 서비스는 정책을 바꿀 수 있으니 제공자별 코드를 격리해야 합니다. 당장 고치기 어려운 장애라면 사용자가 기다리기 전에 상태를 알려야 합니다.
dev.to 반응
- @kanunilabs — 오해를 부르는 오류 메시지 대목이 정말 와닿습니다. ‘인증 실패’는 쓸 만한 진단처럼 들리지만, 네트워크 시간 초과와 DNS 오류, 실제 자격 증명 문제가 전부 같은 메시지로 처리되면 완전히 엉뚱한 방향으로 이끌 수 있습니다. 요청이 연결조차 하지 못한다는 사실을 알아내기 전에 자격 증명을 두 번 교체한 사례는 괴롭습니다. 제공자 어댑터를 둔 방식도 좋습니다. 외부 서비스의 정책 변경을 완전히 막을 수는 없지만, 제공자별 코드를 분리해 두면 나중에 교체하기가 훨씬 편합니다.
- @georgekobaidze — 그래서 적절한 오류 처리와 로깅이 모든 애플리케이션에서 중요합니다. 일반적인 제공자 어댑터는 제공자를 바꾸거나 문제를 진단할 때 모두 도움이 됩니다. 감사합니다!
- @makeev — “오류 메시지가 내내 거짓말을 하고 있었고, 그 메시지도 제가 썼습니다”라는 문장을 책상 위에 붙여두고 싶습니다. 저희도 한 단계 아래에서 똑같은 거짓말을 했습니다. SEC가 상장 티커 전체 목록을 공개하는데, 저희 야간 작업은 목록에 없으면 상장 폐지로 처리했습니다. 그러다 거래량이 하루 수천만 주인 ETF인 QQQ가 파일에서 조용히 빠졌고, 거래가 한창인데도 상장 폐지 페이지로 넘어갔습니다. 목록에 없다는 사실은 상장 폐지의 증거가 아니었지만, 저희가 그렇게 처리하도록 만들어 놓았던 겁니다. 이제는 며칠 동안 거래량이 없거나 실제 상장 폐지 서류가 있는 등 시장에서도 확인될 때만 상장 폐지 처리합니다. OpenSky 차단은 특정 날짜에 시작됐나요, 아니면 Railway가 처음부터 차단됐는데 예전 토큰이 문제를 감추고 있었나요?
- @georgekobaidze — 저도 그 문장을 책상 위에 붙여야 할 것 같습니다. 왜 더 자세히 쓰지 않았는지 모르겠네요. OpenSky는 잘 모르겠습니다. 앱을 다시 배포했더니 갑자기 비행기가 사라졌습니다.
- @makeev — 자격 증명을 두 번 교체하고 나서야 쓸 수 있는 문장이죠. 배포 직후 비행기가 사라졌다면 새 컨테이너의 외부 IP가 달라졌는지 확인하겠습니다. OpenSky 차단이 새로 생긴 게 아니라, 몇 주 동안 있던 차단이 갑자기 드러난 상황일 수 있습니다.
- @georgekobaidze — 네, 그 생각도 했습니다. 이 문제가 전에 생겼을 때는 두세 번 재배포하면 보통 해결됐습니다. 그래서 호스팅 지역을 바꾸면 다른 IP로 나가 도움이 될 거라고 생각했지만, 그래도 해결되지 않았습니다. 적어도 이제 원인은 알았습니다. 지금은 적절한 대체 제공자를 찾아야 합니다.
- @makeev — 잃기 아쉬운 서비스네요. IP 대역을 통째로 차단하고 허용 목록도 제공하지 않으면 사용자 쪽에서 고칠 방법이 없습니다. 어떤 제공자를 선택할지 궁금합니다.
- @georgekobaidze — 앱을 다시 운영하게 되면 다음 글을 올리겠습니다. 아무 제공자나 고르고 싶지 않아 깊이 조사해야 합니다. 다만 지금은 다른 우선순위가 많아서 한동안 기다려야 할 것 같습니다. 그래도 점검 페이지는 마음에 듭니다. 하하.
- @dexoryn — 정말 ‘인증 실패’라는 오해를 부르는 메시지가 가장 큰 교훈인 것 같습니다. 오류가 실제 TCP 시간 초과를 감춰 자격 증명을 두 번 교체했습니다. 잘못된 관측 가능성이 실제 버그보다 더 많은 시간을 낭비할 수 있다는 좋은 사례입니다.
- @georgekobaidze — 정말 그렇습니다. 적절한 오류 처리를 소홀히 해서는 안 됩니다. 취미 프로젝트든 기업용 애플리케이션이든 마찬가지입니다.
원문: dev.to / 번역·요약: Trawling