Sunsetting Create React App

cansweep·2025년 2월 27일
0
post-thumbnail

🔗 https://react.dev/blog/2025/02/14/sunsetting-create-react-app

이제부터 새로운 앱에 대해 Create React App을 더이상 지원하지 않기 때문에 기존 앱들을 프레임워크로 마이그레이션하는 것을 권장합니다. 대신 프로젝트가 프레임워크에 적합하지 않거나 프레임워크 구축부터 시작하고 싶을 때를 위해 이에 대한 문서도 제공합니다.

2016년에 Create React App을 출시했을 때, 이전까지는 리액트 앱을 빌드하기 위한 명확한 방법이 존재하지 않았습니다.

리액트 앱을 만들려면 여러 가지 도구를 설치하고 JSX, linting, hot reloading 과 같은 기본 기능을 지원하도록 직접 세팅해야 했습니다. 이런 작업을 올바르게 하는 것은 매우 까다로웠기 때문에 커뮤니티에서는 일반적인 세팅들을 추가한 보일러 플레이트를 만들었습니다. 하지만, 보일러 플레이트는 업데이트하기 어려웠고 단편화로 인해 새로운 리액트 기능을 출시하기 어려웠습니다.

Create React App은 이러한 문제를 해결하기 위해 여러 도구들을 하나의 권장 설정으로 통합했습니다. 이로 인해 앱들은 새로운 도구 기능들을 손쉽게 사용할 수 있었고 리액트 팀도 주요한 도구 수정사항들(빠른 리프레시 지원, 리액트 훅 린트 규칙 등)을 광범위하게 제공할 수 있게 되었습니다.

이러한 방식은 매우 인기를 얻어서 현재까지 이러한 방식으로 동작하는 도구들이 하나의 카테고리를 이룰 정도였습니다.

Create React App 지원 종료

Create React App은 시작하기 쉽게 만들어 주지만, 고성능 프로덕션 앱을 구축하기 어렵게 만드는 몇 가지 한계가 있습니다. 원칙적으로, 이러한 문제들을 해결하기 위해 프레임워크로 발전시켜야 했으나 현재 유지보수자가 없고 이미 문제를 해결한 많은 프레임워크가 존재했기 때문에 Create React App을 지원 중단하기로 결정했습니다.

오늘부터, 새로운 앱을 설치하면 지원 중단 경고가 뜹니다.

create-react-app is depprecated.

프레임워크를 이용해 새로운 리액트 앱을 만드는 것을 권장합니다. 우리가 추천하는 모든 프레임워크는 클라이언트 전용 SPA를 지원하며 CDN 또는 정적 호스팅 서비스에 서버 없이 배포할 수 있습니다.

기존 앱의 경우 아래 가이드가 클라이언트 전용 SPA로 마이그레이션할 수 있도록 도와줄 것입니다.

Create React App은 유지 보수 모드로 계속 작동할 것이며 React 19에서 동작할 수 있도록 Create React App의 새로운 버전을 출시했습니다.

앱에 특별한 제약이 있거나 자신만의 프레임워크를 구축해 이러한 문제를 해결하고 싶은 경우, 또는 그저 리액트가 어떻게 동작하는지 알고 싶은 경우 Vite나 Parcel을 사용해 커스텀 설정을 구축할 수 있습니다. Vite나 Parcel을 시작하는 데에 도움이 될 수 있도록 프레임워크 구축하기 문서를 발행했습니다.

Create React App의 한계

Create React App과 빌드 도구를 사용하면 리액트 앱을 쉽게 구축할 수 있습니다. npx create-react-app my-app 을 실행하면 개발 서버, 린팅, 프로덕션 빌드가 세팅된 리액트 앱이 만들어집니다.

예를 들어 내부용 어드민을 만든다고 했을 때, 다음과 같이 랜딩 페이지부터 시작할 수 있습니다.

export default function App() {
  return (
    <div>
      <h1>Welcome to the Admin Tool!</h1>
    </div>
  )
}

즉, JSX, 기본 린트 규칙, 개발 및 프로덕션 환경에서 실행할 수 있는 번들러와 같은 기능으로 즉시 코딩을 시작할 수 있습니다.

그러나 이러한 설정에는 실제 프로덕션 앱을 빌드하는 데 필요한 도구가 없습니다. 대부분의 프로덕션 앱들은 라우팅, 데이터 가져오기, 코드 분할과 같은 문제에 대한 해결책이 필요합니다.

Routing

Create React App은 특별한 라우팅 솔루션을 포함하고 있지 않습니다. 처음 시작할 때에는, 경로를 전환하는 방법이 그저 useState를 사용하는 방법 뿐입니다. 그러나 이 방법을 사용하면 특정 페이지의 링크를 공유할 수 없으며(모든 링크가 같은 페이지를 가리키게 됩니다) 시간이 지남에 따라 앱의 구조를 관리하기 어려워집니다.

import { useState } from 'react';

import Home from './Home';
import Dashboard from './Dashboard';

export default function App() {
  // ❌ 상태를 통한 라우팅 관리는 URL을 생성하지 않음
  const [route, setRoute] = useState('home');
  return (
    <div>
      {route === 'home' && <Home />}
      {route === 'dashboard' && <Dashboard />}
    </div>
  )
}

이것이 바로 Create React App 기반의 앱들이 React Router, Tanstack Router와 같은 라우팅 라이브러리를 쓰는 이유입니다. 라우팅 라이브러리를 사용하면 앱에 특정 경로를 정의할 수 있으며 앱의 구조를 관리하기도 쉬워지고 특정 페이지에 대한 링크를 제공할 수도 있습니다.

예를 들어, React Router를 사용하면 route를 다음과 같이 지정할 수 있습니다.

import { RouterProvider, createBrowserRouter } from 'react-router';

import Home from './Home';
import Dashboard from './Dashboard';

// ✅ 각 경로 별로 URL을 가짐
const router = createBrowserRouter([
  {path: '/', element: <Home />},
  {path: '/dashboard', element: <Dashboard />}
]);

export default function App() {
  return (
    <RouterProvider value={router} />
  )
}

위 코드에서는 /dashboard로 이동하는 링크를 공유하면 대시보드 페이지로 이동할 수 있습니다.

라우팅 라이브러리를 사용하면 라이브러리 없이는 구현하기 어려운 중첩 라우트, 라우트 가드, 라우트 전환 등의 기능을 추가할 수 있습니다. 하지만 여기에는 등가교환이 존재합니다. 라우팅 라이브러리를 사용하면 구현하기 어려운 기능들을 추가할 수도 있지만 앱의 복잡성이 증가하기도 합니다.

Data Fetching

Create React App의 또 다른 문제는 데이터 가져오기 입니다. Create React App에는 특별한 데이터 가져오는 방법이 존재하지 않습니다. 가장 일반적인 방법은 useEffect 안에서 fetch를 사용하여 데이터를 가져오는 것입니다.

하지만 이 방법에서는 컴포넌트가 렌더링된 후 데이터를 가져오게 되어 네트워크 워터폴 현상이 발생할 수 있습니다. 네트워크 워터폴은 코드가 다운로드되는 동안 데이터를 병렬로 가져오는 대신, 앱이 렌더링될 때 데이터를 가져오기 때문에 발생합니다.

export default function Dashboard() {
  const [data, setData] = useState(null);

  // ❌ network waterfall 현상 발생
  useEffect(() => {
    fetch('/api/data')
      .then(response => response.json())
      .then(data => setData(data));
  }, []);

  return (
    <div>
      {data.map(item => <div key={item.id}>{item.name}</div>)}
    </div>
  )
}

아무리 데이터를 빨리 가져온다고 하더라도 useEffect 안에서 데이터를 불러오면 사용자가 콘텐츠를 보기 위해 더 오래 기다려야 합니다.

이러한 문제를 해결하기 위해 데이터를 미리 로드하여 컴포넌트가 렌더링되기 전 요청을 보낼 수 있는 React Query, SWR, Apollo, Relay 등의 라이브러리를 사용할 수 있습니다. 이 라이브러리들은 라우팅의 loader 패턴과 같이 사용하면 경로 수준에서 데이터의 의존성을 정의할 수 있어 데이터를 가져오는 작업을 최적화할 수 있습니다.

export async function loader() {
  const response = await fetch(`/api/data`);
  const data = await response.json();
  return data;
}

// ✅ 코드 다운로드와 동시에 데이터를 병렬로 가져옴
export default function Dashboard({loaderData}) {
  return (
    <div>
      {loaderData.map(item => <div key={item.id}>{item.name}</div>)}
    </div>
  )

이러한 방식을 사용하면, 라우터는 초기 페이지 로드 시에 데이터를 즉각적으로 불러올 수 있습니다. 즉, 사용자가 애플리케이션을 탐색할 때 라우터는 경로와 데이터를 동시에 병렬적으로 가져올 수 있습니다. 따라서 콘텐츠가 화면에 보이는 시간을 줄이고 사용자 경험을 향상시킬 수 있습니다.

하지만 이 방식은 로더를 올바르게 정의해야 하고 성능을 위해 복잡도를 증가시켜야 합니다.

Code Splitting

또 다른 문제는 code Splitting 입니다. Create React App은 코드 분할 기능을 제공하고 있지 않습니다. 첫 시작 단계에서는, 코드 분할을 고려하지 않을 수도 있습니다.

이는 곧 애플리케이션이 하나의 번들로 제공된다는 것을 의미합니다.

- bundle.js    75kb

하지만 성능 최적화를 위해서는 코드를 개별 번들로 “분할”하여 사용자가 필요한 코드만 다운로드할 수 있도록 하는 것이 좋습니다. 이렇게 하면 사용자가 보고자 하는 페이지의 코드만 다운로드하게 되므로 애플리케이션 로딩 시간을 단축할 수 있습니다.

- core.js      25kb
- home.js      25kb
- dashboard.js 25kb

코드 분할을 사용하기 위한 방법은 React.lazy 를 사용하는 것입니다.그러나 이 방법은 컴포넌트가 렌더링되기 전까지 코드를 가져오지 않으므로 네트워크 워터폴 현상을 야기할 수 있습니다.

더 나은 해결책은 라우터 기능을 사용해 코드가 다운로드될 때 병렬적으로 코드를 가져오도록 하는 것입니다. 예를 들어 React Router가 제공하는 lazy 옵션을 사용하면 특정 경로에 대해 코드 분할과 최적화를 할 수 있습니다.

import Home from './Home';
import Dashboard from './Dashboard';

// ✅ 렌더링 전에 라우트가 다운로드됨
const router = createBrowserRouter([
  {path: '/', lazy: () => import('./Home')},
  {path: '/dashboard', lazy: () => import('Dashboard')}
]);

최적화된 코드 분할은 올바르게 구현하기 어려워 잘못하면 사용자가 필요한 것보다 더 많은 코드를 다운로드하게 될 수도 있습니다. 코드 분할은 라우터와 데이터 로딩 솔루션을 함께 사용하면 가장 효과적으로 작동하며, 이를 통해 캐싱을 극대화하고, 데이터를 병렬로 가져오며, ‘사용자 상호작용 시 가져오기(import on interaction)’ 패턴을 지원할 수 있습니다.

And more…

이것들은 Create React App의 한계 중 일부 예시일 뿐입니다. 위의 문제들을 해결하고 난 이후에는 대기 상태, 네비게이션 중단, 오류 메세기 표기, 데이터 재검증 등을 고려해야 합니다.

사용자가 해결해야 할 문제들은 다음과 같이 다양한 카테고리로 나뉩니다.

  • 접근성(Accessibility)
  • 애셋 로딩(Asset loading)
  • 인증(Authentication)
  • 캐싱(Caching)
  • 오류 처리(Error handling)
  • 데이터 변경(Mutating data)
  • 내비게이션(Navigations)
  • 낙관적 업데이트(Optimistic updates)
  • 점진적 개선(Progressive enhancement)
  • 서버 사이드 렌더링(Server-side rendering)
  • 정적 사이트 생성(Static site generation)
  • 스트리밍(Streaming)

이 모든 것들이 함께 동작할 때 최적의 로딩 시퀀스를 만들 수 있습니다.

이러한 문제들을 Create React App에서 개별적으로 해결하는 것은 어렵습니다. 각 문제가 서로 영향을 끼치고 있으며 사용자가 익숙하지 않을 수 있는 전문 지식이 필요한 영역들이 포함되어 있기 때문입니다. 문제를 해결하기 위해 결국 사용자는 Create React App 위에 자신만의 맞춤형 솔루션을 구축해야 합니다. 그러나 이 문제는 원래 Create React App이 풀려고 했던 문제이기도 합니다.

Why we Recommend Frameworks

Create React App, Vite, Parcel 과 같은 빌드 도구를 사용해 모든 문제를 직접 해결할 수도 있지만 이는 매우 어렵습니다. Create React App이 빌드 도구를 통합했던 것처럼 사용자 경험을 최적화하기 위해 이런 기능을 하나의 도구로 통합하는 작업이 필요합니다.

빌드, 렌더링, 라우팅, 데이터 가져오기, 코드 분할 등을 통합하는 도구를 “프레임워크” 라고 합니다.

프레임워크는 앱의 구조를 강제함으로써 더 나은 사용자 경험을 제공합니다. 이는 빌드 도구가 더 쉽게 만들기 위해 특정 방식을 따르게 하는 것과 비슷합니다. 이것이 우리가 Next.js, React Router, Expo 등의 프레임워크를 새로운 프로젝트에서 사용하도록 권장하는 이유입니다.

프레임워크는 Create React App과 동일하게 간편한 시작 경험을 제공하면서도 실제 프로덕션 환경에서 직접 해결해야 할 문제에 대한 솔루션을 제공합니다.

profile
하고 싶은 건 다 해보자! 를 달고 사는 프론트엔드 개발자입니다.

0개의 댓글