개발자가 스스로 커스텀한 Hook. 반복되는 로직을 함수로 뽑아내어 재사용할 수 있다.
Custom Hook도 함수형태로 만들며, 리액트이기 때문에 React Hook을 사용할 수 있다.
useState, useEffect, useCallback .. 등등을 사용할 수 있으며, return은 꼭 JSX 코드로 하지 않아도 된다.
HTML 대신에 JavaScript를 사용하여 컴포넌트를 구성하는 것의 장점.
React 18 버전
React 앱들은 Webpack, Rollup과 같은 툴을 사용해 번들링(Bundling)한다.
번들이 된 앱은 모든 JavaScript가 한 곳에 있기 때문에 페이지를 설정하는 데 필요한 호출 수가 적은 태그 하나만 필요했다.
웹 애플리케이션은 대개 하나의 큰 JavaScript 파일로 구성되어 있었다.
이 파일에는 전체 애플리케이션의 코드가 포함되어 있으며, 사용자가 웹 애플리케이션에 처음 접속할 때 이 파일이 다운로드되어야 한다. 하지만 큰 JavaScript 파일을 한 번에 다운로드하는 것은 초기 로딩 시간을 늘릴 수 있고, 사용자가 실제로 필요하지 않은 코드까지 불필요하게 로드하는 문제가 발생할 수 있다.
모던 웹 이전 웹에서는 JavaScript 코드는 최소한의 수준으로 작성되어서 괜찮았다.
하지만, 시간이 지나면서 번들링을 하게 되면 특정 지점에서 코드를 해석하고 실행하는 정도가 느려지게 되었다. 점점 DOM을 다루는 정도가 정교해지며 JavaScript 코드가 방대해졌다. JavaScript 엔진이 해석해야하는 JavaScript 코드의 양이 많아졌다.
이걸 해결하기 위해 코드를 해석하고 실행하는 정도가 느려졌는지 파악해서 번들을 나눈 뒤 지금 필요한 코드만 불러오고 나중에 필요한 코드는 나중에 부른다는 개념이 나왔다.
번들링 되는 파일에는 앱을 만들면서 npm을 통해 다운로드하는 서드파티(Third Party) 라이브러리도 포함된다. 서드파티 라이브러리는 제 3자 라이브러리다. 서드파티 라이브러리는 플러그인이나 라이브러리 또는 프레임워크 등이 존재한다.
이러한 서드파티 라이브러리는 다양한 메서드를 포함하고 있고, 번들링 시 많은 공간을 차지한다. 따라서 사용 중인 라이브러리를 전부 불러와 사용하는 것보다 따로따로 불러와 사용할 수 있다면 차지하는 공간을 줄일 수 있을 것이다.
React는 SPA이어서 사용하지 않는 모든 컴포넌트까지 한 번에 불러오기 때문에 첫 화면이 렌더링 될 때까지의 시간이 오래 걸렸다.
이전에는 코드 파일의 가장 최상위에 import 지시자를 사용해 사용하고자 하는 라이브러리 및 파일을 불러오는 방법을 사용했다. 이것은 static import(정적 불러오기)라고 한다.
이전에는 문서 상위에 있어야 했고, 블록문 안에 위치할 수 없는 제약사항이 있었다. 왜냐하면 번들링 시 코드 구조를 분석해 모듈을 한 데 모으고 사용하지 않는 모듈은 제거하는 등의 작업을 하는데, 코드 구조가 간단하고 고정이 되어 있을 때에야만 이 작업이 가능해지기 때문이었다.
이제는 구문 분석 및 컴파일해야하는 스크립트의 양을 최소화하기 위해 dynamic import 구문을 지원한다.
const [loadedModule, setLoadedModule] = useState(null);
const handleButtonClick = () => {
import('./LazyComponent')
.then((module) => {
// 모듈이 성공적으로 로드되면, 필요한 함수 또는 컴포넌트를 가져옵니다.
const { LazyComponent } = module;
setLoadedModule(<LazyComponent />);
})
.catch((error) => {
console.error('Error while loading lazy component:', error);
});
};
then을 이용해 불러온 모듈에서 필요한 코드만 가져온다. 가져온 코드에 대한 호출은 해당 함수 내부에 있어야 한다.
이 방식은 번들링 시 분할된 코드(청크)를 지연 로딩시키거나 요청 시에 로딩할 수 있다.
브라우저에서 네트워크 탭을 들어가서 보면 제일 큰 번들 JavaScript인 bundle.js로 만들어져서 통신을 하고, 그 다음 dynamic import가 포함된 컴포넌트가 불리면 그 다음에서야 분리된 chunk 번들 js 파일이 번들된다.
코드 분할을 기반으로 작동하는 것들을 알아보자.
// Lazy로 로드할 컴포넌트를 지정합니다. () => import('./MyComponent')와 같은 함수를 전달합니다.
const LazyComponent = React.lazy(() => import('./MyComponent'));
이 React.lazy로 감싼 컴포넌트는 단독으로 쓰일 수 없고, React.suspense 컴포넌트 하위에서 렌더링해야 한다.
import React, { lazy, Suspense } from 'react';
// Lazy로 로드할 컴포넌트를 지정합니다. () => import('./MyComponent')와 같은 함수를 전달합니다.
const LazyComponent = lazy(() => import('./MyComponent'));
const App = () => {
return (
<div>
<h1>Lazy Loading Example</h1>
<Suspense fallback={<div>Loading...</div>}>
{/* Suspense 컴포넌트로 Lazy로 로드할 컴포넌트를 감쌉니다. */}
<LazyComponent />
</Suspense>
</div>
);
};
export default App;
Suspense 컴포넌트의 fallback prop은 컴포넌트가 로드될 때까지 기다리는 동안 로딩 화면으로 보여줄 React 엘리먼트를 받아들인다.
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>
);
코드 분할은 웹 페이지를 불러오고 진입하는 단계인 Route에 이 기능들을 적용시키는 것이 좋다.
이런 정보를 찾고 있었습니다, 감사합니다.