React SPA의 초기 성공과 한계
Safari Portal은 럭셔리 여행사 및 투어 운영사를 위한 여정 빌더로, React Create App, Redux, React Router, Axios, i18n 등을 활용한 대규모 React 애플리케이션과 Rails REST API 백엔드로 구성되었습니다. 5년 전 React를 선택한 이유는 당시 React의 높은 인기, 성숙한 생태계, 그리고 Turbo나 Hotwire 같은 Rails 프런트엔드 솔루션의 부재 때문이었습니다. 초기에는 풍부한 사용자 인터페이스, 브라우저 내 PDF 생성, 핫 리로딩을 통한 우수한 개발자 경험 등의 장점을 누렸습니다.
그러나 시간이 지남에 따라 여러 문제점이 드러났습니다.
-
개발 복잡성: Redux를 사용한 보일러플레이트 코드 작성, 최대 15분까지 늘어난 빌드 시간, 프런트엔드와 백엔드 간의 배포 조정 및 API 계약 관리로 인한 복잡성이 증가했습니다.
-
생산성 저하: 각 작업마다 React 로직과 Rails 백엔드 간의 지속적인 컨텍스트 전환이 필요하여 개발 효율성이 떨어졌습니다.
전환의 계기: CRM으로의 확장과 AI 시대의 요구
2024년, Safari Portal은 단순한 여정 빌더를 넘어 작업 관리, 연락처 관리, 재무 통합, 맞춤형 양식 빌더 등을 포함하는 풀 사이클 CRM으로 확장해야 하는 상황에 직면했습니다. 예산과 12명의 개발자 팀 규모는 유지된 채 12개월 안에 4개의 새로운 모듈을 구축해야 했습니다. 이러한 상황에서 React는 병목 현상이 되었습니다. 하나의 기능을 개발하기 위해 백엔드, 프런트엔드 개발자, 관리자의 조율, 계약 설계, 상태 관리, 통합 테스트 등 많은 리소스가 필요했습니다.
동시에 AI 기술(ChatGPT, Copilot 등)의 등장은 이해관계자들의 개발 속도 및 버그 허용치에 대한 기대를 높였습니다. AI가 빠르게 코드를 작성할 수 있다면, 새로운 화면을 추가하는 데 2주가 걸리는 이유에 대한 질문이 제기되었고, 버그에 대한 관용도도 제로가 되었습니다.
Rails Hotwire로의 전환 결정과 구현
이러한 배경 속에서 팀은 Hotwire 기반의 Rails 프로젝트에서 성공적인 경험을 쌓았고, 이를 Safari Portal에 적용하기로 결정했습니다. 기존 프로젝트를 처음부터 다시 작성할 수 없었기에, React와 Rails 간의 인증 공유, 배포 조정, 라우트 충돌 관리, 일관된 UI/UX 제공 등 여러 위험을 감수해야 했습니다. 그러나 잠재적인 이점이 위험보다 크다고 판단했습니다.
기술적 구현: 기존 React 기능은 그대로 유지하고, 새로운 기능은 Rails와 Hotwire로 개발하는 하이브리드 접근 방식을 채택했습니다.
-
아키텍처 변경: 기존에는 Nginx가 React 컨테이너로 요청을 라우팅하고, React가 다시 Nginx를 통해 Rails API에 접근하는 방식이었습니다. 새로운 방식에서는 Kamal 프록시가 모든 요청을 Rails 컨테이너로 라우팅하고, Rails가 정적 파일(React 빌드 포함)과 동적 콘텐츠를 모두 제공합니다.
-
React를 정적 파일로: 개발 환경에서는 Rails 컨트롤러가 React 개발 서버(포트 8080)로 리디렉션하고, 프로덕션에서는 Rails의
public폴더에서 React 빌드 파일을 제공합니다. 배포 시 React는 독립적으로 빌드된 후 Kamal 배포 프로세스를 통해 Rails Docker 이미지 내public폴더로 복사됩니다. -
인증 공유: Rails에서는 Rodauth를 사용하여 쿠키 기반 세션을 관리하고, React에서는 JWT 토큰을 사용합니다. Rails 로그인 후, 사용자를 특별한
JWT pass컨트롤러로 리디렉션하여 Turbo를 이용해 React에 필요한 액세스 토큰과 리프레시 토큰을 로컬 스토리지에 설정한 다음, 원래 요청된 URL로 다시 리디렉션하여 두 환경 모두에서 인증된 상태를 유지합니다. -
React 컴포넌트 임베딩:
turbo_mountGem을 사용하여 Rails 애플리케이션 내에 React 컴포넌트를 효과적으로 삽입할 수 있었습니다. -
Rails 기능을 React에 임베딩: React 애플리케이션 위에 Rails 모델 창을 iframe으로 렌더링하여, 서버 사이드 렌더링의 이점을 활용하면서도 React 앱 내에서 Rails 기능을 원활하게 사용할 수 있었습니다. 공유 인증 덕분에 iframe 내에서도 인증 상태가 유지됩니다.
결과 및 교훈
이러한 전환을 통해 다음과 같은 긍정적인 결과를 얻었습니다.
-
생산성 향상: Rails는 ‘1인 개발자 프레임워크’로 기능하며, 고객 피드백이 몇 시간 내에 프로덕션에 반영되고, 버그 수정 및 피드백 대응이 하루 안에 가능해졌습니다. A/B 테스트 및 기능 플래그 설정도 매우 간단해졌습니다.
-
개발자 효율성: 한 명의 개발자가 추가 도움 없이 처음부터 끝까지 작업을 완료할 수 있게 되어, 12명의 개발자가 동시에 12개의 다른 작업을 수행할 수 있게 되었습니다. 결과적으로 월간 주요 작업 수가 25-30개에서 50-60개로 두 배 증가했습니다.
-
AI 활용: Rails의 ‘컨벤션 오버 컨피규레이션’ 덕분에 Copilot 및 Claude와 같은 AI 도구가 Rails 코드를 더 잘 이해하여 생산성을 더욱 높였습니다.
직면한 어려움: JavaScript 생태계가 React 중심으로 과도하게 집중되어 있어, React Dates나 React Select와 같은 훌륭한 라이브러리의 바닐라 JS 대안이 부족했습니다. 또한, 많은 서드파티 서비스 SDK가 React 전용이었습니다. React 전문 개발자들은 Stimulus와 같은 Rails 프런트엔드 도구에 익숙해지는 데 학습 곡선과 일시적인 생산성 저하를 겪었습니다. Stimulus는 React에 비해 방대한 예시나 커뮤니티 지원이 부족한 편입니다.