20. Layout, Dynamic loading (lazy, suspense)

random-olive·2023년 2월 10일
0

프로젝트 01 : 

목록 보기
18/25

아이콘과 전체적인 페이지 색감 등 테스트를 마쳤으니 바로 페이지를 완성하고싶지만,
경험상 Layout 구조를 만든 후 페이지를 구현하는 것이 재사용과 관리 측면에서 좋았어서
다음의 순서로 개발을 진행한다.

  • Layout 완성
  • dynamic import + 로딩바 구현
  • 컴포넌트 + 랜딩페이지 제작 + 서브메뉴바
  • 컴포넌트 + 컨텐츠 페이지 제작

1. Layout & Template

  • 현재 나의 프로젝트 디렉토리 구성은 다음과 같은 Atomic Design Pattern을 따르고 있다.
  • 템플릿 폴더에 Layout(페이지의 뼈대)을 지정해놓고, 필요할 때 쓰려고 한다.
atoms: 단일 컴포넌트 (ex: 검색바, 검색 버튼)
molecules: atom의 묶음 (검색바 + 검색버튼)
organisms: molecule의 묶음 -> 구역 (검색창 구역)
templates: 컨텐츠가 채워지지 않은 skeleton
pages: 컨텐츠로 채워진 templates

2. Layout & Routes

  • 하려는 것 : 우선 Basic Layout을 생성하려고 한다. Basic Layout은 랜딩페이지 및 다른 페이지에 쓰이는 기본 뼈대로, 헤더, 메뉴바, (가변 요소), 푸터 구조를 고정시켜둔다. 페이지마다 해당 레이아웃을 불러오고, 가변 요소만 다르게 하면 매번 페이지 제작시에 같은 컴포넌트를 불러와서 뼈대를 제작할 필요가 없어 재사용성을 높이고 코드 중복을 줄일 수 있다.

  • Layout을 제작 후, 가변 요소는 react-router-dom 패키지의 "Outlet"이라는 요소로 넣을 수 있다.

  • 웹페이지 경로에 따른 보이는 화면 구현은 react-router-dom 패키지의 'BrowserRouter, Routes, Route'로 구현할 수 있다.

  • 우선, react-router-dom을 설치한다.

npm i react-router-dom
  • Header + 메뉴바 + Footer를 Basic Layout으로 하고, 내부를 조금씩 변화시켜줄 것이다. 이럴 경우 Outlet을 사용하여 다음과 같이 짤 수 있다.
//Layouts.tsx
import {Outlet} from 'react-router-dom;

import Header from 'components/molecules/Header';
import Menu from 'components/molecules/Menu';
import Footer from 'components/molecules/Footer';

export const Basic = () => {
	return (
    	<>
        <Header />
        <Menu />
        <Outlet />     (1)
        <Footer />
        </>
    )
  );
};
  • (1) Outlet부분이 변화되는 부분이고, 나머지가 고정되는 컴포넌트 모음이다.
  • Outlet은 Routes, Route와 함께 활용할 것이고, 이를 에러없이 구현하기 위해서는 BrowserRouter로 App 컴포넌트 전체를 감쌀 필요가 있다.
//root폴더의 index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';

import { ThemeProvider } from 'styled-components';
import { myTheme } from 'styles/theme/DefaultTheme';
import { BrowserRouter } from 'react-router-dom';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);

root.render(
  <React.StrictMode>
    <ThemeProvider theme={myTheme}>
      <BrowserRouter> (1)
        <App />
      </BrowserRouter>
    </ThemeProvider>
  </React.StrictMode>
);
  • (1) BrowserRouter 태그로 App 컴포넌트를 감쌌다.
  • 만든 Outlet과 Routes, Route는 다음과 같이 활용한다.
//App.tsx
import {Basic} from 'components/templates/Layouts';
import PATH from 'constants/routePath';
import LandingPage from 'pages/LandingPage';

function App() {
  return (
    <>
      <div className='App'>
          <Routes> (1)
            <Route element={<Basic />}>  (2)
              <Route path={PATH.MAIN} element={<LandingPage />} /> (3)
            </Route>
          </Routes>
      </div>
    </>
  );
}

export default App;
  • (1) Route는 Routes로 묶어야 문제 없이 구현된다.
    Layout을 사용하기 위해서 Route를 이중구조로 한다.
  • (2) 부모 쪽 Route에 element로 들어가는 부분이 고정되는 컴포넌트다. Basic 컴포넌트에서 헤더-메뉴-Outlet-푸터 구조를 만들었으니 Basic 컴포넌트를 넣어준다.
  • (3) 자식 쪽 Route에 element로 들어가는 부분이 Outlet에 들어가는 부분이다. 경로 '/'와 랜딩페이지에 대한 element 설정을 해준다.

3. Dynamic import (lazy, suspense)

  • (static import)
import Component from './Component';

가장 상위에서 import 구문을 사용해서 불러오는 방식.
앱의 빌드 타임에 '사용하지 않는 컴포넌트까지' 한꺼번에 로딩해 초기 렌더링 지연시간이 길어질 수 있다.

  • (dynamic import)
    React.lazy(), Suspense를 사용해서 불러오는 방식.
    런타임에 '사용하는 모듈만'을 로딩해 초기 렌더링 지연시간을 줄인다.
    다만, 페이지를 이동하는 과정마다 로딩 화면이 보여질 수 있어 서비스에 따라서 적용여부를 결정하는 것이 좋다.
//App.tsx
import {Suspense, lazy} from 'react';
import { Routes, Route } from 'react-router-dom';
import { Basic } from 'components/templates/Layouts';
import PATH from 'constants/routePath';
import { LoadingBunny } from 'components/atoms/Loading';

import LandingPage from 'pages/LandingPage';를 다음과 같이 수정
const LandingPage = lazy(() => import('pages/LandingPage')); (2)

function App() {
  return (
    <>
      <div className='App'>
        <Suspense fallback={<LoadingBunny />}> (1)
          <Routes>
            <Route element={<Basic />}>
              <Route path={PATH.MAIN} element={<LandingPage />} /> (2)
            </Route>
          </Routes>
        </Suspense>
      </div>
    </>
  );
}

export default App;
  • (1) Suspense의 fallback prop은 컴포넌트가 로드될 때까지 로딩화면으로 보여줄 React 엘리먼트를 받아들인다.
  • (2) React.lazy()로 감싼 컴포넌트는 단독으로 쓰일 수 없고, React.suspense의 컴포넌트 하위에서 렌더링해야 한다.
profile
Doubts kills more dreams than failure ever will

0개의 댓글