2021. 12. 19. 17:39ㆍFrontend/React
라우팅 구현 중에, 경로에 따라 유동적으로 컴포넌트를 호출하여 보여줄 방법을 고민하다가
대표적인 성능 개선 기법인 코드분할(code splitting)방식을 응용하여 적용한 내용입니다.
코드 분할이란?
코드 분할은 여러분의 앱을 “지연 로딩” 하게 도와주고 앱 사용자에게 획기적인 성능 향상을 하게 합니다. 앱의 코드 양을 줄이지 않고도 사용자가 필요하지 않은 코드를 불러오지 않게 하며 앱의 초기화 로딩에 필요한 비용을 줄여줍니다.
목표
1. 목록에서 선택한 값(=ID)에 따라 path 설정하여 라우팅
2. 전달받은 route param(=ID) 기준으로 필요한 컴포넌트 lazy loading하여 렌더링
구현
우선 프로젝트 구조는 아래와 같습니다.
path 경로에 따라 projects 폴더 하위의 경로에 있는 index.js를 호출하는게 저의 목표입니다.
예시
- /projects/1 -> projects/1/index.js 호출
- /projects/2 -> projects/2/index.js 호출
react-router-dom을 사용하여 라우팅 구현을 해주기 위해,
App.js에는 아래와 같이 BrowserRouter 설정을 해두었으며, Home이라는 컴포넌트에 링크 설정을 해두었습니다.
전체 코드가 궁금하신 분들은 아래 '더보기'를 눌러 코드를 확인해주세요.
// App.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { BrowserRouter as Router } from "react-router-dom";
ReactDOM.render(
<React.StrictMode>
<Router>
<App />
</Router>
</React.StrictMode>,
document.getElementById('root')
);
// Home.js
import logo from '../logo.svg'
import '../App.css';
import {useState} from "react";
import {Link} from 'react-router-dom';
function Home() {
const [projects] = useState([{
id: 1,
name: 'project1',
date: '2021.12.06',
}, {
id: 2,
name: 'project2',
date: '2022.01.06',
}])
return (
<div className="App">
<header className="black-nav">
<img src={logo} alt="logo" style={{width: '30px'}}/>
<div>React Portfolio</div>
</header>
<div className="main-container">
{
projects.map((project, i) => {
const {id, name, date} = project
return (
<div className="project" key={i}>
<h3>
<Link to={`/project/${id}`}>{name}</Link>
</h3>
<p>{date}</p>
</div>
)
})
}
</div>
</div>
);
}
export default Home;
1. 목록에서 선택한 값(=ID)에 따라 path 설정하여 라우팅
우선, react-router-dom 공식문서 내용에 따라 중첩 라우팅(Nested Routing)을 구현해봅시다.
예제와 동일하게 설정하였으며, 선택 값의 ID를 :projectId로 선언하여 Project의 parameter로 전달합니다.
import './App.css';
import Home from './routes/Home'
import {Route, Switch} from 'react-router-dom';
function App() {
return (
<Switch>
<Route exact path="/">
<Home/>
</Route>
<Route path='/project/:projectId'>
<Project/>
</Route>
</Switch>
);
}
function Project() {
const {projectId} = useParams();
console.log(projectId)
// TODO 필요한 컴포넌트를 import해 리턴할 예정입니다.
return <></>
}
export default App;
2. 전달받은 route param(=ID) 기준으로 필요한 컴포넌트 lazy loading
위에 명시한 스캐폴딩 구조에 따라, 전달받은 parameter(=ID) 기준으로 모듈을 import 해봅시다.
단 미리 import하는 방식은 비효율적이므로, 필요할 때만 유동적으로 호출 할 수 있도록 React.lazy를 활용하겠습니다.
React 코드분할 공식문서 내용에 따라 아래와 같이 설정하였습니다.
- React.lazy 함수를 사용하면 동적 import를 사용해서 컴포넌트를 렌더링 할 수 있습니다.
=> Project 내에서 ID 기반으로 경로를 설정하여 컴포넌트를 렌더링합시다.
(*참고로 index는 생략 가능합니다. 상위 폴더까지 지정하면 자동 호출 됨)
- 참고로 lazy loading되는 모듈은 반드시 default export 되어야합니다.
function Project() {
const {projectId} = useParams();
const Component = lazy(() => import(`./projects/${projectId}`))
return <Component/>
}
여기까지하면 에러가 발생될텐데, 그 이유는
lazy 컴포넌트는 Suspense 컴포넌트 하위에서 렌더링되어야 하기 때문입니다.
- Suspense는 lazy 컴포넌트가 로드되길 기다리는 동안 로딩 화면과 같은 예비 컨텐츠를 보여줄 수 있게 해줍니다.
=> 우리도 예제와 같이 로딩 중임을 알려줍시다.
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<Switch>
<Route exact path="/">
<Home/>
</Route>
<Route path='/project/:projectId'>
<Project/>
</Route>
</Switch>
</Suspense>
);
}
그럼 끝 👍