프론트엔드 성능 최적화와 관련된 이야기가 나올 때, 자주 언급되는 개념들이다.
1. Lazy Loading
필요한 js 파일은 필요할 때 불러오자는 개념이다.
<button id="loadScriptBtn">스크립트 로드</button>
<script>
document.getElementById('loadScriptBtn').addEventListener('click', function() {
if (!document.getElementById('myLazyScript')) { // 이미 로드되었는지 확인
var script = document.createElement('script');
script.id = 'myLazyScript';
script.src = '/lazy-script.js';
script.onload = function() {
console.log('스크립트가 성공적으로 로드되었습니다.');
// 스크립트 로드 후 실행할 함수 호출 등
};
script.onerror = function() {
console.error('스크립트 로드에 실패했습니다.');
};
document.body.appendChild(script);
}
});
</script>
위의 html에서 스크립트 로드라는 버튼을 클릭했을 때, js파일을 html에 삽입한다.
삽입된 js파일은 브라우저에 의해 다운로드되고, 브라우저에서 해당 스크립트 태그가 로드되면 특정 함수를 시작할 수 있다.
이처럼 무거운 js나 css 파일, 그 외 무거운 리소스들을 필요할 때 불러오는 개념이 지연 로딩이다.
2. Code Splitting
코드 스플리팅은 코드를 기능별, 모듈별로 잘 나눠서 필요할 때 필요한 파일만 불러와서 쓸 수 있게 만드는 기법이다.
위의 lazy-script.js 파일처럼 사용자가 필요한 시점에 불러올 수 있는 js 파일로 분리된 기능들을 말한다.
반면 위의 script 태그는 html에 삽입되어 html을 읽을 때 함께 로드되게 된다.
3. Tree Shaking
NodeJS에서 개발을 하다보면, 각종 의존성을 불러와서 개발해야 한다.
이 때, 파일 전체를 불러오는 경우가 있고, 파일의 일부를 불러오는 경우가 있다.
트리 쉐이킹이란 사용하는 코드만을 번들러에 포함시키고, 나머지는 버려 성능을 확보하는 기법이다.
// 필요한 함수만 명시적으로 import 합니다.
import { format, addDays } from 'date-fns';
import { ko } from 'date-fns/locale';
const today = new Date();
const threeDaysLater = addDays(today, 3);
console.log(format(threeDaysLater, 'yyyy년 MM월 dd일 EEEE', { locale: ko }));
위의 date-fns라는 라이브러리는 모듈별로 잘 분리되어 있다.
빌드 시 번들러는 구문분석을 통해 이 기능에서 꼭 필요한 함수들만 빌드에 포함시켜 chunk파일의 용량 증가가 적다.
import moment from 'moment';
// import 'moment/locale/ko';
// moment 객체를 생성하여 사용
const today = moment();
const threeDaysLater = today.add(3, 'days');
console.log(threeDaysLater.format('YYYY년 MM월 DD일 dddd'));
위의 라이브러리는 moment라는 라이브러리이다. 예전에는 많이 썼었는데, 요새는 거의 안쓴다.
위처럼 라이브러리의 진입점을 로딩해서 쓰도록 되어있어서 빌드 시, 라이브러리에서 쓰지 않는 기능들도 chunk파일에 포함된다.
이로 인해 용량의 증가가 매우 큰 편이다.
React에도 관련된 기능들이 있다.
import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
// 1. React.lazy()를 사용하여 페이지 컴포넌트들을 동적으로 import 합니다.
const HomePage = lazy(() => import('./pages/HomePage'));
const AboutPage = lazy(() => import('./pages/AboutPage'));
const ProfilePage = lazy(() => import('./pages/ProfilePage'));
function App() {
return (
<Router>
{/* 2. Suspense 컴포넌트로 Routes를 감싸고, fallback UI를 지정합니다. */}
<Suspense fallback={<div>페이지를 불러오는 중입니다...</div>}>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/about" element={<AboutPage />} />
<Route path="/profile" element={<ProfilePage />} />
</Routes>
</Suspense>
</Router>
);
}
export default App;
위처럼 코드레벨에서 동적으로 코드 스플리팅을 번들러에 지시할 수 있다.
위의 페이지들은 별도의 chunk-**.js 파일로 분할되어 빌드될 것이다.
그리고 html에서 버튼으로 동적인 스크립트를 불러왔듯이 UI에서 라우팅 버튼을 클릭했을 때, 동적으로 페이지를 로드하게 된다.
React와 같은 Client Side Rendering 방식에선 url을 입력했을 때, 얼마나 빠르게 첫페이지가 로드되는지가 중요하다.
사용자가 홈페이지에 접속한 적이 없을 때, 브라우저는 첫 화면에 필요한 모든 파일을 다운로드해야하고, 그것을 그려야 한다.
사용자가 바로 필요로 하지 않는 정적 파일들은 나중에 다운로드 받고, 필요한 파일만 먼저 다운로드하는 것이다.
이런 방식으로 초기 페이지 렌더링 속도에 대한 부담을 조금이나마 줄일 수 있다.
'공부' 카테고리의 다른 글
[BE] Publish & Subscribe 구조 (0) | 2025.06.04 |
---|---|
[알고리즘] 문제 분석 (1) | 2025.06.03 |
[웹] Server Sent Event (0) | 2025.06.02 |
[포트폴리오] 동영상 플랫폼 (0) | 2025.05.29 |
[Spring-Data-JPA] N + 1 문제 (0) | 2025.05.26 |