React Router 6.0 마이그레이션
React Router 6.0 마이그레이션 (React Router 6.0 Migration)
Backstage는 오랫동안 react-router 버전 6.0.0-beta.0을 사용해 왔습니다.
출처: 문서
본문
Backstage는 오랫동안 react-router 버전 6.0.0-beta.0을 사용해 왔습니다. 우리가 이 불안정한 버전을 채택한 이유는 v6에 Backstage와 잘 맞는 새 기능, 특히 상대 라우팅(relative routing)이 있었기 때문입니다. 이 초기 불안정 버전에 일찍 뛰어들었기 때문에, 언젠가는 react-router v6의 안정 버전으로의 파괴적인 마이그레이션이 필요하리라는 것을 알고 있었는데, 바로 지금이 그 시점입니다!
이 마이그레이션은 필수이지만 각 앱이 통제합니다. 즉 앱을 언제 마이그레이션할지 직접 선택합니다. 다만 미래의 어느 시점에는 react-router의 베타 버전 지원을 중단하게 될 것이며, 그때는 마이그레이션을 강제로 하게 됩니다.
React Router v6 안정 버전은 여러 개선 사항과 버그 수정을 가져옵니다. 특히 경로를 해석하는 방식이 개선되어 /catalog와 /catalog-import 같은 경로가 혼동될 수 있던 버그가 수정되었습니다.
마이그레이션 (Migration)
1단계 - Backstage 1.6으로 업그레이드
react-router v6을 지원하는 첫 Backstage 릴리스는 1.6입니다. 마이그레이션을 시작하기 전에 먼저 이 버전으로 업그레이드해야 합니다. 얼리 어답터라면 그 릴리스 이전에도 1.6.0-next.1에서 마이그레이션을 시도해 볼 수 있습니다.
2단계 - react-router를 peerDependencies로 이동
프로젝트에 한 번에 하나의 버전의 react-router만 설치되도록 하는 것이 중요합니다. react 버전을 처리하는 방식과 유사하게, 이제 모든 플러그인과 패키지는 React Router 의존성에 대해 직접 의존성이 아니라 peer 의존성을 선언합니다. 유일한 예외는 앱 패키지(packages/app/package.json)인데, 이곳에 프로젝트에서 사용할 React Router 버전을 결정하는 직접 의존성이 있습니다.
내부 패키지가 package.json에 react-router나 react-router-dom에 대한 의존성을 지정하고 있다면, 이것들을 peerDependencies로 변환해 앱 package.json에서 react-router 버전을 제어할 수 있게 하는 것이 중요합니다.
다음 명령을 실행해 이 단계를 자동화할 수 있습니다.
yarn backstage-cli migrate react-router-deps
수동으로 하고 싶다면 packages/app/package.json이나 다른 앱 패키지를 제외한 모든 package.json 파일에 아래 변경을 적용하세요. 아직 존재하지 않는 의존성은 옮기지 말고, dependencies와 devDependencies 모두를 옮기세요.
package.json
dependencies { ...- "react-router-dom": "^6.0.0-beta.0",- "react-router": "^6.0.0-beta.0" }, peerDependencies: { ...+ "react-router-dom": "6.0.0-beta.0 || ^6.3.0",+ "react-router": "6.0.0-beta.0 || ^6.3.0" },
3단계 - 외부 플러그인이 업데이트되었는지 확인
외부 플러그인도 동일한 peerDependencies 업데이트를 수행해야 하므로 최신 버전으로 업데이트하는 것이 중요합니다.
이 마이그레이션 중에 업데이트가 필요한 외부 플러그인이 있을 수 있습니다. @backstage 범위 밖에 있는 플러그인 중 설치와 호환되지 않는 플러그인을 발견하면, 해당 플러그인의 GitHub 저장소에 기존 이슈가 있는지 확인하거나 새 이슈를 올리세요.
4단계 - 앱의 React Router 의존성 올리기
이제 실제로 최신 React Router 버전으로 마이그레이션할 차례입니다. 이 글을 쓰는 시점의 최신 버전은 6.3.0이지만, 물론 이는 계속 변하는 목표입니다.
첫 단계는 packages/app/package.json을 수정하는 것입니다.
package.json
- "react-router": "6.0.0-beta.0",- "react-router-dom": "6.0.0-beta.0",+ "react-router": "^6.3.0",+ "react-router-dom": "^6.3.0",
프로젝트에 앱 패키지가 여러 개 있다면 그 모든 패키지에 동일한 변경을 적용하세요.
변경 후 yarn install을 실행하고, 이어서 yarn why react-router를 실행해 설치를 검증합니다. 로그에서 유일한 결과로 다음 줄이 보여야 합니다.
=> Found "[email protected]"
여러 항목이 보이거나 특히 => Found "[email protected]"가 보인다면 의존성이 아직 React Router v6 안정 버전을 완전히 지원하도록 마이그레이션되지 않은 것입니다. Yarn why 명령이 기록한 정보로 위 단계를 다시 확인하세요. yarn why react-router-dom에도 동일한 과정을 반복하세요.
프로젝트 전체를 안정 버전으로 깔끔하게 옮기지 못해 어려움을 겪는다면, 루트 package.json에서 Yarn "resolutions" 오버라이드를 사용할 수 있습니다. 이 옵션은 런타임에서 숨겨진 손상을 일으킬 수 있으니 피하려고 하세요. 오버라이드가 필요한 플러그인을 반드시 검증하세요. 더 나은 방법은 플러그인이 업데이트될 시간이 될 때까지 잠시 마이그레이션을 미루는 것입니다.
5단계 - 파괴적 변경
npx @backstage/create-app으로 만든 새 앱이라면 위 단계가 전부입니다. 내부 플러그인과 커스터마이징을 만들었다면 React Router 변경 로그의 파괴적 변경을 검토하고 앱의 모든 부분을 검증하세요. 아래에서 가장 중요한 파괴적 변경을 요약했습니다.
파괴적 변경 (Breaking Changes)
파괴적 변경의 전체 목록은 변경 로그를 참조하세요. 아래에서는 몇 가지 가장 중요한 것들을 강조합니다.
라우트 경로 (Route paths)
Route 컴포넌트는 항상 path 또는 index prop을 포함해야 합니다.
<Routes> {/* Invalid */} <Route element={<Example />} /> {/* Valid */} <Route path="/" element={<Example />} /> {/* Valid but discouraged due to incompatibility with react-router beta */} <Route index element={<Example />} /></Routes>
각 Routes 요소 내의 절대 라우트 경로는 이제 자신의 위치와 일치해야 합니다. 즉 다음은 유효하지 않습니다.
<Routes> <Route path="/foo"> <Route path="/bar" /> {/* INVALID, must be "/foo/bar" or "bar" */} </Route></Routes>
Routes와 Route 컴포넌트
Routes와 Route 컴포넌트는 둘 다 큰 관련 파괴적 변경을 받았습니다. 이제 Routes 요소의 자식으로는 Route 요소와 React 프래그먼트 외에는 아무것도 가질 수 없습니다. 즉 다음과 같은 구조는
<Routes> <MyComponent path="/foo" /> ...</Routes>
다음과 같이 마이그레이션해야 합니다.
<Routes> <Route path="/foo" element={<MyComponent />} /> ...</Routes>
Routes 변경과 다소 관련되어, 더 이상 Route 요소를 Routes 래퍼 밖에서 단독으로 렌더링할 수 없습니다. 이전에는 그런 Route 요소를 렌더링하면 element prop 내용이 대신 렌더링되었지만, 이제는 오류를 던집니다.
<Navigate /> 컴포넌트
React Router v6 안정 버전으로 마이그레이션할 때 브라우저 콘솔에서 Navigate 컴포넌트에 대한 경고가 보일 수 있습니다. 이는 Navigate 컴포넌트를 element prop에 넣어 Route 컴포넌트로 감싸야 합니다.
{/* prettier-ignore */ /* highlight-remove-next-line */}<Navigate key="/" to="catalog" />;{/* prettier-ignore */ /* highlight-add-next-line */}<Route path="/" element={<Navigate to="catalog" />} />;
NavLink
NavLink 컴포넌트는 더 이상 activeClassName과 activeStyle prop을 갖지 않습니다. 대신 className과 style prop이 링크 활성 여부를 나타내는 부울을 받는 콜백을 수락합니다.
플러그인 작성자를 위해 (For Plugin Authors)
게시된 플러그인을 마이그레이션할 때 염두에 둬야 할 몇 가지가 있습니다. 물론 위에서 설명한 대로 React Router 의존성을 peerDependencies로 이동해야 합니다. 또한 플러그인이 런타임에서 React Router 두 버전 모두와 정말 호환되는지 확인해야 합니다. 이를 위해 다음 추가 지침을 따를 수 있습니다.
-
자신의 프로젝트에서
react-router와react-router-dom버전을 올려 안정 버전을 사용하세요. 플러그인이 단일 패키지 프로젝트라면devDependencies에 넣으세요. 안정 버전이 더 엄격하므로 작업 기준으로 더 좋습니다. -
모든
Route요소에pathprop이 있는지 확인하세요. 베타 버전이 지원하지 않으므로 새indexprop은 사용하지 마세요.Routes내의 index 라우트에는path="/"를 사용하세요. -
NavLink를 사용한다면 새 API와 옛 API를 동시에 사용하고 TypeScript 오류는 우회해서 해결하세요.
문제 해결 (Troubleshooting)
브라우저 콘솔에서 React Router 관련 오류 메시지를 확인하세요.
yarn.lock에서 이전 버전의 react-router에 의존하는 패키지를 확인하세요.
yarn why react-router