FrontEnd

기존 프로젝트 react v17 -> v18 업그레이드 로그

hayjo 2022. 4. 24. 22:00

React v18이 나왔다는 기쁜 소식에 기존 프로젝트의 react와 react-dom 버전을 최신으로 올리려고 봤더니

Conflicting peer dependency 에러가 뜬다.

@reduxjs/toolkit에서 react@"^16.14.0 || ^17.0.0"만 지원하도록 설정해두었기 때문이다.

최신 버전으로 올리면 React v18을 지원하는지 살펴보니 최근에 업데이트가 되었다.

 

Release v1.8.1 · reduxjs/redux-toolkit

This release updates RTK's peer dependencies to accept React 18 as a valid version. This should fix installation errors caused by NPM's "install all the peer deps and error if they don't match" be...

github.com

그럼 toolkit 버전을 올리면 되겠지?

npm install @reduxjs/toolkit@latest

그러려면 먼저 react-redux 버전을 올려줘야 한다.

npm install react-redux@latest

그랬더니 redux-toolkit 버전이 낮아서 안 된다는 에러가 뜬다.

닭이 먼저냐 달걀이 먼저냐를 해결하려면 둘을 동시에 업데이트해줘야 한다.

npm install react-redux@latest @reduxjs/toolkit@latest

리덕스가 해결되었다.

잊지말고 @testing-library/react 버전도 올려줘야 한다.

올리지 않고 테스트를 돌리면 이런 워닝이 뜬다.

npm install @testing-library/react@latest

 

업데이트를 마치고 프로젝트를 확인해보는데 라우터가 작동하지 않는다. 살펴보니 이런 이슈가 있다. 

https://github.com/facebook/react/issues/21674

 

React 18: react-router@v5 is breaking in the Strict Mode (strict effects) · Issue #21674 · facebook/react

remix-run/react-router#7870 I do not have permission to post https://github.com/reactwg/react-18/discussions. Please open and pin a new issue in that repo to list all widely-used library that does ...

github.com

react-router-dom@v5에서 동작하지 않는다고 한다. 이것도 버전을 올려준다.

npm install react-router-dom@latest

react-router-dom은 API 변화가 있다.

 

업데이트하고 공식문서의 변경사항을 참고해서 바뀐 부분을 수정한다.

  • <Switch /> -> <Routes />
  • <Redirect>s inside <Switch> -> <Route path="/sth" element={<Navigate to="path"} />} />
  • useHistory -> useNavigate와 관련 api 변경사항
  • 그리고 regex 지원 범위 축소 대응

기존에 지원되던 ? 옵션이 빠져서 약간 당황했지만,

/:parent/:child? 를 /:parent와 /:parent/:child로 각각 쪼개서 넣어주면 정상적으로 작동하는 걸 확인할 수 있다.

useParams를 쓰면 /:parent일 때 child는 undefined로 들어오기 때문에 문제 없다.

 

내 경우 child가 없는 경우를 default로 처리하려했던 거라서, 이제는 /parent에서 따로 redirect를 해주는 게 더 나을 것 같다.

하지만 이건 리팩토링에서 추가로 다루기로 하고 지금은 넘어간다.

 

react-router-dom까지 버전 업데이트를 하고 나니 드디어 React v18에서 제대로 작동한다.

아직 React18의 기능을 직접 써본 건 없지만 일단 한 걸음 내딛었다는데 의의를 두기로 한다.

 

덧붙임

React 18을 지원하지 않는 dependency 처리 이야기

 

프로젝트 기존 dependency 중에 react-dnd-multi-backend가 있었다.

작업 도중 드래그 앤 드롭 부분에서 react-dnd의 도움을 받았는데, react-dnd는 마우스와 터치 동시 지원이 안 되는 아쉬움이 있다.

물론 react-dnd-touch-backend를 이용하고 enableMouseEvents 옵션을 주면 사용가능하지만, 문서에서 언급하고 있듯이 버그가 좀 있다.

이 문제를 해결하기 위해 도입한 것이 react-dnd-multi-backend.

처음 작업 당시엔 터치 디바이스를 지원할 생각이 없었어서 깊게 생각하지 않았다가, 프로젝트 마무리 시점에 모바일도 지원하면 좋을 것 같고, 마침 적합한 라이브러리가 있어서 깊게 고민하지 않고 도입했었던 기억이 난다.

 

그런데 React 버전을 올리려니까 Conflicting peer dependency 이슈가 있다.

  • react-dnd-multi-backend에서 react18을 지원해줄 때까지 버전 업데이트를 미루거나
  • react-dnd-multi-backend를 걷어내고 대체재를 물색하거나

결정을 해야했다.

물론 react-dnd-multi-backend에 직접 PR을 올리는 게 가장 이상적인 해결책이겠지만, 쉽지 않을 것 같고 그렇게까지 리소스를 투입하기는 어려워서 다른 방법을 찾기로 했다.

검색을 좀 해보니 마땅한 라이브러리 대체재는 보이지 않았다.

그럼 여기서 스톱?

생각해보니 JS단에서 touch device를 감지하는 방법이 분명 있을 것 같다.

 

그럼 redux에 backend를 세팅하는 시점에 디바이스 종류를 파악해서 걸맞은 라이브러리를 넣어주면 되지 않을까?

라는 생각에 도달, 검색해보니 무려 11년 전에 올라온 답변이 있다.

whats-the-best-way-to-detect-a-touch-screen-device-using-javascript

isTouchDevice 헬퍼를 이용해서 backend를 조건부로 넣는 것에 성공.

이제 react-dnd-multi-backend 의존성이 사라졌으니 해당 라이브러리를 들어내도 된다.

import { HTML5Backend } from "react-dnd-html5-backend";
import { TouchBackend } from "react-dnd-touch-backend";
import App from "./App";
import store from "./app/store";
import isTouchDevice from "./helpers/device";

const backend = isTouchDevice() ? TouchBackend : HTML5Backend;
const container = document.getElementById("root");
const root = createRoot(container);

root.render(
  <React.StrictMode>
    <Provider store={store}>
      <DndProvider backend={backend}>
        <Router>
          <App />
        </Router>
      </DndProvider>
    </Provider>
  </React.StrictMode>,
);

그러고보니 처음부터 이렇게 했어도 됐을 것 같다.

물론 임시방편이고, 마우스와 터치를 둘다 지원하는 디바이스가 있다면 한쪽만 먹힐 테니 최종적으로는 멀티를 처리하는 방향으로 가야하겠지만.

대신 이 방법은 dynamic import를 할 수 있으니 속도가 조금 향상될 수 있을 것 같기도 하고.

 

이런 부분을 고려하면, 라이브러리를 선택할 때 peer dependency 여부/볼륨도 중요하다.

모든 프로젝트가 제때 업데이트되는 건 아니니까, 특정 라이브러리에 의존하게 되면 전체 업데이트 사이클이 종속돼버린다.

no dependency 라이브러리들이 인기를 끌고 있는 이유를 직접 체감할 수 있었던 좋은 경험이었다.