운영 저널
멈춘 배포를 다시 파일 전송까지 복구한 기록
정적 웹 화면이 열린다는 사실과 파일 전송 시스템이 살아 있다는 사실은 달랐습니다. 이 글은 원본 비밀값과 계정 식별자를 제외하고, 운영자가 실제로 따라간 복구 순서와 저장소가 뒷받침하는 결정을 기록합니다.
근거 범위 운영자가 제공한 장애 연대기, Railway 설정 파일, 공개 health/info 점검 스크립트, 통합 테스트
이번 장애의 첫 인상은 모순적이었습니다. PC에서는 웹 화면이 열렸지만 휴대폰은 연결되지 않았습니다. Vibe Share의 화면은 Cloudflare Pages에서, 세션 생성과 Socket.IO 연결은 Railway의 API에서 제공되기 때문에 두 현상은 동시에 일어날 수 있습니다. 정적 화면의 200 응답을 전체 서비스 정상으로 판단한 것이 첫 번째 함정이었습니다.
운영 기록에는 Railway 체험 기간이 끝난 뒤 API와 연결된 Postgres·Redis 서비스가 오프라인이 되었고, API 경로에서 Application not found가 보인 시점이 남아 있습니다. 민감한 로그 원문과 서비스 식별자는 공개하지 않았습니다. 저장소만으로 그 오류의 정확한 플랫폼 내부 원인을 재현할 수는 없으므로, 여기서는 관찰한 현상과 실제 복구 작업만 구분해 적습니다.
복구 연대기
- 화면과 API를 분리해 확인했습니다.PC 웹이 열리는지와 별개로
/health,/api/info, 새 세션 생성 응답을 확인했습니다. 이 순서 덕분에 UI 문제가 아니라 relay 계층 문제라는 범위를 먼저 좁혔습니다. - 데이터 서비스를 먼저 되살렸습니다.오프라인이 된 Postgres와 Redis를 다시 배포한 뒤 API가 참조할 연결 정보를 점검했습니다. 애플리케이션만 먼저 재시작하면 migration과 세션 캐시가 다시 실패할 수 있기 때문입니다.
- pre-deploy에서 멈춘 지점을 따로 다뤘습니다.이 저장소의 Railway 설정은 앱 시작 전에
npm run db:migrate를 실행합니다. 실제 복구 중 이 단계가 실패해 애플리케이션 시작까지 가지 못했습니다. 보존된 공개 기록에는 오류 원문이 없으므로 특정 SQL이나 자격 증명을 원인이라고 단정하지 않습니다. - API와 커스텀 도메인을 순서대로 복구했습니다.migration이 통과하고 API 프로세스가 health check를 받을 수 있게 한 다음
api.getvibeshare.com경로가 현재 서비스로 연결되는지 확인했습니다. 도메인만 정상이어도 뒤의 서비스가 없으면 전송은 작동하지 않습니다. - health 다음에 읽고 쓰는 흐름을 확인했습니다.드라이버 이름만 보는 데서 끝내지 않고 세션 생성, 코드 join, Socket.IO room 참가, 수락과 다운로드 상태가 오가는 통합 테스트를 실행했습니다. 이것이 ‘프로세스가 켜짐’과 ‘파일 전송 경로가 동작함’을 구분하는 마지막 단계였습니다.
What we observed — 우리가 관찰한 것
Application not found는 휴대폰 카메라나 QR 이미지 문제가 아니었습니다. PC 화면을 제공하는 호스트와 세션을 만드는 API 호스트가 분리되어 있어, 앞쪽 화면은 정상이면서 뒤쪽 연결만 실패할 수 있었습니다. 또한 Railway의 pre-deploy 실패는 앱 로그가 생기기 전 단계에서 배포를 막으므로, 실행 중인 Node.js 문제처럼 조사하면 시간을 잃습니다.
공개 /health 응답은 active database, cache, realtime, storage 드라이버와 fallback 경고를 보여 줍니다. 하지만 그 응답 하나가 실제 파일 한 건의 전체 상태 전이를 보장하지는 않습니다. 그래서 운영 점검과 자동 통합 테스트를 별도 층으로 유지합니다.
What we changed — 우리가 바꾼 것
저장소 루트를 Railway 소스로 유지하고, railway.toml에 migration, start command, healthcheck path를 명시했습니다. 서버 앱이 workspace의 shared package를 사용하기 때문에 apps/server만 별도 루트로 잡지 않는 결정입니다. 운영 점검 스크립트는 공개 URL과 active driver가 예상값인지 비교하며, fallback이 있으면 성공으로 처리하지 않습니다.
복구 절차도 ‘웹 화면 → API health → API info → Railway 상태와 로그 → Postgres/Redis → R2 → 도메인’처럼 관찰 가능한 경계별로 정리했습니다. 모든 단계를 한 번에 재배포하는 대신 어느 경계에서 실패했는지를 남기는 방식으로 바꿨습니다.
What remains limited — 아직 제한되는 것
이 기록에는 Railway 계정 화면이나 원본 장애 로그를 게시하지 않았습니다. 따라서 체험 기간 종료와 pre-deploy 실패 사이의 플랫폼 내부 인과를 일반화하지 않습니다. 또 health와 자동 테스트가 통과해도 특정 이동통신망, 특정 Safari 버전, 큰 파일의 장시간 백그라운드 전송까지 보장하지 않습니다.
데이터 서비스와 API가 다시 내려가면 정적 PC 화면만 남는 같은 형태의 장애가 재발할 수 있습니다. 현재 방어선은 상태 분리, health/readback 확인, 새 세션으로 끝까지 전송해 보는 절차이며, 무중단 운영을 약속하는 다중 리전 구성은 아닙니다.