Railway가 Next.js에서 프론트엔드를 이전했습니다: 빌드 시간 10분에서 2분 이하로 단축

We moved Railway's frontend off Next.js. Builds went from 10+ mins to under 2

요약

Railway는 전체 프로덕션 프론트엔드를 Next.js에서 Vite + TanStack Router로 마이그레이션하여 빌드 시간을 10분에서 2분 이하로 단축했으며, 단 2개의 PR로 무중단 배포를 완성했다. Railway의 애플리케이션은 클라이언트 중심이며 웹소켓과 실시간 기능이 많아서 Next.js의 서버 우선 패러다임이 필요하지 않았다. TanStack Router의 타입 안전 라우팅, 1급 레이아웃, 빠른 개발 루프가 더 적합했으며, 300개 이상의 리다이렉트와 보안 헤더 설정을 Nitro 서버로 통합했다.

핵심 포인트

  • 빌드 시간 단축 원인: Next.js 빌드 중 '페이지 최적화 완료' 단계에서 6분 중 3분이 소요되었는데, 이는 클라이언트 중심의 Railway 대시보드에서 불필요한 오버헤드였다. Vite로 전환하면서 이 단계가 제거되어 총 빌드 시간이 10분 이상에서 2분 이하로 단축되었다.
  • 아키텍처 불일치: Pages Router 기반의 레이아웃 처리가 불편했고, App Router는 서버 우선 패러다임을 강조하여 Railway의 클라이언트 중심 제품과 맞지 않았다. 결국 프레임워크의 기본 설계가 실제 사용 패턴과 충돌했다.
  • 타입 안전 라우팅: TanStack Router는 라우트 파라미터와 검색 파라미터를 타입 추론하며, 자동완성이 전체 라우트 트리에서 작동한다. 파일 시스템 기반 라우트 생성으로 라우팅 설정이 더 명시적이고 안전해진다.
  • 1급 레이아웃: 경로 없는 레이아웃 라우트로 기존의 해킹 성격의 해결책들을 합성 가능하고 예측 가능한 패턴으로 대체했다. 이는 UI 구조를 더 명확하게 표현할 수 있게 해준다.
  • 개발 속도 개선: 즉각적인 HMR(Hot Module Replacement)과 거의 0에 가까운 시작 시간으로 코드 변경과 결과 확인 사이의 피드백 루프가 사실상 사라졌다. 이러한 빠른 반복이 팀의 생산성에 큰 영향을 미친다.
  • 무중단 마이그레이션: PR 1에서 next/image, next/head, next/router 등 Next.js 의존성을 모두 제거하고, PR 2에서 프레임워크 교체를 수행했다. 200+ 라우트를 일요일 이른 아침에 병합하고 즉시 팀이 검증하여 같은 날 수정사항을 배포했다.
  • 서버 통합: Nitro를 서버 레이어로 추가하여 next.config.js의 역할을 대체했으며, 500개 이상의 리다이렉트, 보안 헤더, 캐싱 규칙을 한 곳에서 관리하도록 통합했다.
  • 트레이드오프: next/image의 기본 제공 최적화를 포기하고 Fastly 엣지 최적화로 대체했으며, next-seo, next-sitemap 같은 도구도 경량의 내부 구현으로 교체했다. TanStack Start의 완성도는 Next.js보다 낮지만, 개선 방향과 유지보수팀의 반응성이 좋다고 평가했다.

왜 중요한가

클라이언트 중심 애플리케이션의 개발자들에게 프레임워크 선택이 얼마나 중요한지 보여주며, 올바른 스택 선택이 개발 속도와 빌드 성능에 얼마나 큰 영향을 미칠 수 있는지를 실제 사례로 입증한다.

📄 전문 번역

Railway의 대규모 프론트엔드 마이그레이션: Next.js에서 Vite + TanStack Router로

Victor Ramirez | 2026년 4월 3일

Railway의 프로덕션 프론트엔드 전체가 더 이상 Next.js 위에서 돌아가지 않습니다. 대시보드, 캔버스, railway.com – 모든 게 이제 Vite + TanStack Router로 움직이고 있어요. 단 2개의 PR로 마이그레이션을 끝냈고, 다운타임은 제로였습니다.

Next.js는 좋았다. 그다음부턴 아니었다

Next.js는 Railway를 영점에서 월간 수백만 사용자를 서빙하는 프로덕션 앱으로 만들어주었습니다. 정말 훌륭한 프레임워크죠. 하지만 어느 순간 우리 제품에는 맞지 않는 도구가 되어버렸어요.

빌드 시간이 10분을 넘어섰거든요. 이중 6분이 Next.js 자체 때문이고, 그중 절반은 "페이지 최적화 마무리(finalizing page optimization)" 단계에서 멈춰있었습니다. 하루에 여러 번 배포하는 팀 입장에선 이 정도의 빌드 시간은 단순 불편함이 아닙니다. 매번 반복할 때마다 상당한 시간 비용을 지불하는 셈이죠.

Railway의 앱은 거의 전부가 클라이언트 사이드입니다. 대시보드는 풍부한 상태를 가진 인터페이스고, 캔버스는 실시간으로 동작합니다. WebSocket이 곳곳에 있어요. 그런데 Next.js의 서버 우선(server-first) 패러다임은 우리가 쓰지 않는 부분이었습니다. Pages Router 위에 우리만의 추상화 레이어를 덧붙여서 레이아웃과 라우팅 문제를 해결해야 했거든요.

Pages Router에 머물러 있으니 공유 레이아웃이 항상 거슬렸습니다. 모든 레이아웃 패턴이 그때그때 임시방편이었지, 프레임워크가 기본으로 지원하는 기능이 아니었던 거죠. App Router로 옮기면 이런 문제가 해결되겠지만, 그건 서버 우선 패턴에 더 무겁게 기울어져 있습니다. 우리 제품은 의도적으로 클라이언트 중심으로 설계되어 있으니, 그걸 도입하려면 필요하지도 않은 패러다임으로 전체를 다시 짜야 할 판이었어요.

왜 TanStack Start + Vite인가

우린 실제로 우리가 만드는 방식에 맞는 스택을 원했습니다. 명확하고, 클라이언트 우선이고, 빠른 반복이 가능한 것 말이에요. 게다가 이 스택으로 개발하는 게 정말 즐겁습니다. 제품 팀이 원했던 몇 가지 특징들이 우리를 결정적으로 설득했어요.

타입 안전 라우팅이 기본으로 제공됩니다. 라우트 파라미터와 검색 파라미터가 자동 추론되고, 자동완성이 전체 라우트 트리에서 작동합니다. 라우트 자체도 파일 시스템에서 생성되죠.

레이아웃이 일급 기능입니다. 경로 없는 레이아웃 라우트로 모든 임시방편이 대체되었고, 이제 조합 가능하고 예측 가능한 형태가 되었습니다.

개발 루프가 빨라서 생각할 필요가 없을 수준입니다. HMR이 즉시 반영되고, 시작 시간은 거의 무시할 수준이에요. 코드를 변경하고 결과를 보는 사이의 피드백 사이클이 사실상 사라집니다.

정말 필요한 부분만 SSR합니다. 마케팅 페이지, 변경 로그, 채용 공고처럼요. 나머지는 순수 클라이언트 사이드인데, 서버 렌더링의 이점이 없는 화면에 굳이 강제할 필요가 없거든요. (편집자 주: 아이러니하게도 이 블로그 자체는 아직 TanStack으로 옮겨지지 않았네요.)

명시적인 모델입니다. 즉, TanStack은 프레임워크의 "마법"을 덜 사용하고 내부 동작을 더 많이 제어할 수 있게 설계되어 있습니다.

우리 팀의 몇몇이 휴가 때 TanStack Start를 시도해봤는데 반응이 일치했습니다. 이걸로 개발하는 게 좋고, Railway의 대시보드 같은 제품에는 벤치마크만큼 중요합니다.

2개 PR, 다운타임 제로

선택을 내린 후 본격적으로 시작했습니다. 머지 전 스쿼시하기 전엔 아마 수백 개의 커밋을 만들었을 겁니다.

수백만 사용자를 서빙하는 프로덕션 프론트엔드를 200개 이상의 라우트로 마이그레이션하는 건 보통 수개월에 걸쳐 병렬 운영하고 점진적으로 전환하는 작업입니다. 우린 일정이 빠듯했으니 2개의 PR로 끝내버렸습니다.

첫 번째 PR은 모든 Next.js 특화 기능을 제거했습니다. next/image, next/head, next/router 같은 것들 말이에요. 각각을 네이티브 브라우저 API나 프레임워크 무관한 대체재로 바꿨습니다. 이 PR은 프레임워크 자체를 바꾼 게 아닙니다. 단지 모든 Next.js 의존성을 제거해서 두 번째 PR이 깔끔한 프레임워크 교체가 되도록 한 거죠.

두 번째 PR은 프레임워크 자체를 바꿨습니다. 200개 이상의 라우트를 마이그레이션했어요. 라우팅과 무관한 모든 것을 페이지 파일에서 개별 React 컴포넌트로 먼저 추출한 다음, 원래 페이지 트리에서 모든 라우트를 생성했습니다.

그 다음 서버 레이어로 Nitro를 추가했고, next.config.js를 Nitro 설정으로 대체했습니다. 500개 이상의 리다이렉트, 보안 헤더, 캐싱 규칙이 한곳에 통합되었어요. Next.js가 제공하던 폴리필(Buffer, url.parse 등)을 브라우저 네이티브 대체재로 바꿨는데, 덤으로 코드도 더 깔끔해졌습니다.

일요일 아침 일찍 머지했습니다. 팀이 즉시 Discord 라이브 전쟁실에서 테스트를 시작했고, 수정 사항들이 그날 안에 계속 배포되었어요. 다운타임은 없었습니다.

우리가 포기한 것들

더 빠르고 명시적인 스택을 얻었지만, 대가는 있었습니다.

이미지 최적화 기능. next/image를 일반 <img> 태그와 Fastly의 엣지 이미지 최적화로 대체했습니다.

생태계의 일부. next-seo, next-sitemap 같은 도구들을 작은 인하우스 대체재로 만들었어요. 그리 복잡하지 않고 추가 의존성도 줄었습니다.

성숙도. TanStack Start는 새로운 도구라 거친 부분들이 있을 수 있습니다. 하지만 방향이 맞고, 메인테이너들이 반응이 빠르고, 우리가 Vite와 TanStack을 스폰서하고 있으니 괜찮습니다. 그들의 방향성을 믿거든요.

Railway의 프론트엔드는 Railway 위에서 실행됩니다

우린 우리의 프로덕션 프론트엔드를 사용자들과 같은 방식으로 운영합니다. PR마다 프리뷰 배포, 헬스 체크...