-
[React] 심화FE 2023. 7. 19. 22:41
Custom Hooks
➡️ 스스로 커스텀한 훅을 의미하고 이를 이용해 반복되는 로직을 함수로 뽑아내어 재사용할 수 있음. 여러 url을 fetch할 때 여러 input에 의한 상태 변경 등 반복되는 로직을 동일한 함수에서 작동하게 하고 싶을 때 주로 사용
- 상태관리 로직의 재활용 가능
- 클래스 컴포넌트보다 적은 양의 코드로 동일한 로직을 구현할 수 있음
- 함수형으로 작성하기 때문에 보다 명료 (e.g. useSomethting)
function FriendStatus(props) { const isOnline = useFriendStatus(props.friend.id); if (isOnline === null) { return 'Loading...'; } return isOnline ? 'Online' : 'Offline'; } function FriendListItem(props) { const isOnline = useFriendStatus(props.friend.id); return ( <li style={{ color: isOnline ? 'green' : 'black' }}> {props.friend.name} </li> ); }
FriendStatus, FriendListItem 동일하게 사용하는 로직을 분리하여 함수 usFriendStatus로 만든 후 Custom Hook을 적용함.
(Custom Hook 내부에 useState와 같은 리액트 내장 Hook 사용 가능) Custom Hook을 사용했다고 두 개의 컴포넌트가 같은 state를 공유하는 것은 아님. 로직만 공유하며 state는 컴포넌트 내에서 독립적으로 정의되어 있음
1. Custom Hook을 정의할 때 함수 이름 앞에 use를 붙이는 것이 규칙
2. 대개의 경우 프로젝트 내의 hooks 디렉토리에 Custom Hook을 위치시킴
3. Custom Hook을 만들 때 함수는 조건부 함수가 아니어야함. return 하는 값이 조건부여서 안 되며 이 useFriendStatus Hook은 온라인 상태 여부를 boolean 타입으로 반환.
※ React의 주목해야할 기능 : React 18에서는 이전 문법을 사용해서 안 되며 createRoot API를 사용한다. Concurrent Feature가 있으며 그 중 Suspense 기능을 독립적으로 렌더링할 수 있도록 되어있음
코드 분할 (Code Spliting)
- 대부분 리액트 앱들은 Webpack, Rollup과 같은 툴을 사용해 번들링(Bundling)함
- HTML 웹 페이지에 자바스크립트를 쉽게 추가할 수 있기 때문
➡️ 번들링을 하게 되면 특정 지점에서 코드를 해석하고 실행하는 정도가 느려지면서 코드 자체가 방대해지고 무거워짐. 이러한 것을 코드 분할로 여러 번들을 동적으로 만들고 불러와 Webpack, Rollup과 같은 번들러가 지원해 당장 필요한 코드가 아니라면 따로 분리시켜 나중에 필요할 때 불러서 사용할 수 있음. (대규모 프로젝트의 앱인 경우에 페이지 로딩 속도 개선)
번들 분할 혹은 줄이는 법
➡️ 번들링 되는 파일에 npm을 통해 다운로드하는 서드파티(Third Party) 라이브러리도 포함됨. 잘 사용하면 효율적인 개발할 수 있지만 다양한 메서드를 제공해서 코드의 양이 많고 번들링 시 많은 공간을 차지함
/* 이렇게 lodash 라이브러리를 전체를 불러와서 그 안에 들은 메서드를 꺼내 쓰는 것은 비효율적입니다.*/ import _ from 'lodash'; ... _.find([]); /* 이렇게 lodash의 메서드 중 하나를 불러와 쓰는 것이 앱의 성능에 더 좋습니다.*/ import find from 'lodash/find'; find([]);
React에서의 코드 분할
➡️ 리액트는 SPA(Single-Page-Application)인데 사용하지 않는 모든 컴포넌트까지 한 번에 불러와 첫 화면이 렌더링 될 때까지 오랜 시간이 걸림. 사용하지 않는 컴포넌트를 나중에 불러오기 위해 코드 분할 dynamic import(동적 불러오기)를 도입함 (이전에는 코드 파일의 최상위에서 import 지시자를 사용하여 라이브러리 및 파일을 불러왔는데 이를 static import 정적 불러오기라고 했음)
Static Import
/* 기존에는 파일의 최상위에서 import 지시자를 이용해 라이브러리 및 파일을 불러왔습니다. */ import moduleA from "library"; form.addEventListener("submit", e => { e.preventDefault(); someFunction(); }); const someFunction = () => { /* 그리고 코드 중간에서 불러온 파일을 사용했습니다. */ }
이전엔 import 구문은 문서 상위에 위치해야 했음. 번들링 시 코드 구조를 분석해 모듈을 한 데 모으고 사용하지 않는 모듈을 제거하는 등 작업해야 하는데 코드 구조가 간단하고 고정이 되어 있을 때에만 작업이 가능하기 때문
Dynamic Import
form.addEventListener("submit", e => { e.preventDefault(); /* 동적 불러오기는 이런 식으로 코드의 중간에 불러올 수 있게 됩니다. */ import('library.moduleA') .then(module => module.default) .then(someFunction()) .catch(handleError()); }); const someFunction = () => { /* moduleA를 여기서 사용합니다. */ }
mduleA가 다른 곳에서 사용되지 않을 때, 사용자가 form을 통해 양식을 제출한 경우에만 가져올 수 있도록 함.
then 함수를 사용해서 필요한 코드만 가져오고 가져온 코드는 모든 호출은 해당 함수 내부에 있어야 하며 번들링 시 분할된 코드를 지연 로딩 시키거나 요청시에 로딩할 수 있게 됨. (React.lazy와 함께 사용 가능)
React.lazy()
➡️ dynamic import를 사용해 컴포넌트를 렌더링할 수 있으며 초기 렌더링 지연시간을 어느 정도 줄일 수 있음
import Component from './Component'; /* React.lazy로 dynamic import를 감쌉니다. */ const Component = React.lazy(() => import('./Component'));
단독으로는 사용 불가하며 React.suspense 컴포넌트 하위에서 렌더링을 해야함
React.Suspense
➡️ Router로 분기가 나누어진 컴포넌트들을 위 lazy를 통해 import하면 해당 path로 이동할 때 컴포넌트를 불러오는데 이 과정에서 로딩하는 시간이 생김. Suspense는 아직 렌더링이 준비되지 않는 컴포넌트가 있을 때 화면을 보여주며 로딩이 완료되면 렌더링이 준비된 컴포넌트를 보여주는 기능
/* suspense 기능을 사용하기 위해서는 import 해와야 합니다. */ import { Suspense } from 'react'; const OtherComponent = React.lazy(() => import('./OtherComponent')); const AnotherComponent = React.lazy(() => import('./AnotherComponent')); function MyComponent() { return ( <div> {/* 이런 식으로 React.lazy로 감싼 컴포넌트를 Suspense 컴포넌트의 하위에 렌더링합니다. */} <Suspense fallback={<div>Loading...</div>}> {/* Suspense 컴포넌트 하위에 여러 개의 lazy 컴포넌트를 렌더링시킬 수 있습니다. */} <OtherComponent /> <AnotherComponent /> </Suspense> </div> ); }
Suspense 컴포넌트 하나로 여러 개의 lazy 컴포넌트를 보여줄 수 있음
React.lazy와 Suspense의 적용
➡️ 웹 페이지를 불러오고 진입하는 단계인 Route에 이 두 기능을 적용시키는 것이 좋음
import { Suspense, lazy } from 'react'; import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; const Home = lazy(() => import('./routes/Home')); const About = lazy(() => import('./routes/About')); const App = () => ( <Router> <Suspense fallback={<div>Loading...</div>}> <Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> </Routes> </Suspense> </Router> );
라우터가 분기되는 컴포넌트에서 각 컴포넌트에 React.lazy를 사용해 import한 후, Route 컴포넌트들을 Suspense로 감싼 후 로딩화면으로 사용할 컴포넌트를 fallback 속성으로 설정
'FE' 카테고리의 다른 글
Component Library (0) 2023.07.23 React Custom Component (0) 2023.07.20 [React] 심화 (0) 2023.07.18 [솔로 프로젝트] (0) 2023.07.17 [솔로 프로젝트] (0) 2023.07.14