코드 스플리팅은 웹 애플리케이션의 성능을 개선하기 위한 기법 중 하나로, 전체 애플리케이션을 작은 청크로 나누어 필요할 때마다 로드할 수 있도록 하는 방식이다. 이 방법은 애플리케이션의 초기 로딩 시간을 크게 줄일 수 있으며, 사용자 경험을 향상시키는데 기여한다.
IDE( 또는 코드에디터) 에서 작성한 자바스크립트 파일 하나가 모듈 하나다.
개발자는 export
와 import
를 사용해서 모듈(파일) 간 코드를 공유한다.
자바스크립트 버전이 ES5 에서 ES6 으로 넘어오면서 변화 중 하나는 모듈화가 가능해졌다는 점이다.
ES5 까지는 전역 스코프
만 가능했지만, ES6 부터는 모듈 스코프
가 가능해져 특정 파일만 불러올 수 있다.
다음은 정적으로 import
하는 법이다.
import { FieldValues, SubmitHandler, useForm } from 'react-hook-form';
이와 같이 선언된 스크립트는 실제로 사용되지 않더라도 전부 불러오게 된다.
다음은 동적으로 import
하는 법이다.
const { useForm } = await import('react-hook-form');
const { register } = useForm({ mode: 'onChange'});
이와 같이 동적으로 선언된 스크립트는, 런타임시 실제로 필요할 때 스크립트 파일을 볼러올 수 있다.
동적 import
로 작성된 코드는 WebPack, Rollup, Vite 등 코드 스플리팅을 지원하는 번들러에서 bundle 시 코드가 나누어져 번들링 된다.
이렇게 코드를 나누어 번들링하고, 런타임시 필요한 모듈을 불러오게 하는 것을 코드 스플리팅(Code Splitting) 이라고 한다.
성능 최적화
사용자가 실제로 필요로 하는 코드만 로드하기 때문에 불필요한 데이터 전송을 줄이고, 애플리케이션의 응답성을 향상시킨다.
효울적인 자원 관리
처음 애플리케이션을 로드할 때 모든 스크립트를 다운로드할 필요가 없으므로, 네트워크 자원과 처리 파워를 절약할 수 있다.
유지 관리의 용이성
애플리케이션을 작은 청크로 나누어 관리하면, 특정 부분의 수정이 전체 애플리케이션에 미치는 영향을 최소화할 수 있다.
다음은 리모델링 프로젝트 때 썼던 라우팅 방법이다.
{
id: 'Main',
path: '/',
text: '메인 페이지',
lazy: async () => {
const Module = await import('@/pages/Main/Main');
return { Component: Module.default };
},
}
다음은 부캠 파이널 프로젝트 때 썼던 라우팅 방법이다.
const router = createBrowserRouter([
// 루트 페이지 (메인)
{
path: '/',
element: <RootLayout />,
errorElement: <MissingPage />,
children: [
{
index: true,
element: <IntroPage />,
},
{
path: 'main',
element: (
<SetPage>
<MainPage />
</SetPage>
),
}
둘의 차이점은 뭘까?
리모델링 프로젝트
비동기로 컴포넌트를 로드하는 코드 스플리팅이다. lazy
키워드를 통해 비동기 함수로 컴포넌트를 로드하고 있으며, 페이지 로드 시간을 줄이기 위해 사용된다. 해당 페이지가 필요할 때만 해당 컴포넌트를 로드하게 해서 초기 로딩 속도를 개선한다.
파이널 프로젝트
createBrowserRouter
를 사용하여 라우터를 정의하고 있다.
createBrowserRouter란?
React Router v6
에서 도입된 API 중 하나로, 라우터를 생성한다. 더 선언적이고 직관적인 방식으로 라우트를 정의할 수 있게 해준다.
또한 코드의 가독성과 유지 보수성을 향상시킨다.
선언적 라우팅 구조
라우트를 배열 형태로 선언적으로 정의할 수 있다. 각 라우트는 경로, 컴포넌트, 자식 라우트 등을 포함할 수 있으며, 이러한 구조는 애플리케이션의 라우팅 로직을 한 눈에 파악하기 쉽게 만들어 준다.
네스팅된 라우트
라우트 내에 children
속성을 사용하여 하위 라우트를 정의할 수 있다. 이를 통해 복잡한 라우팅 구조를 계층적으로 관리할 수 있으며, 각 레벨에서 필요한 컴포넌트를 렌더링할 수 있다.
에러 핸들링
errorElement
프로퍼티를 사용하여 특정 경로에서 발생할 수 있는 오류를 처리하는 컴포넌트를 정의할 수 있다. 이는 사용자에게 보다 친절한 에러 페이지를 제공하고, 예외 상황을 더욱 효과적으로 관리할 수 있게 해준다.
리모델링 프로젝트
단일 lazy
함수를 통해 비동기적으로 모듈을 로드하고, 모듈에서 기본으로 내보낸 컴포넌트를 사용한다. 이 방법은 라우트별로 모듈을 분리하여 필요할 때만 로드하는 코드 스플리팅을 통해 성능 최적화를 꾀할 수 있다.
파이널 프로젝트
라우트별로 element
, errorElement
, children
구조를 사용하여, 더 세밀하고 계층적인 라우팅 구조를 제공한다. 각 라우트는 자체적으로 페이지 레이아웃, 에러 페이지, 하위 라우트 등을 정의할 수 있다. 이는 라우트 관리를 좀 더 유연하게 하며, 복잡한 애플리케이션에서 라우트 관리를 쉽게 할 수 있게 한다.
리모델링 프로젝트
초기 로드 시간에 집중한 지연 로딩 방식을 사옹하여, 사용자가 실제로 방문하는 페이지의 컴포넌트만 로드한다.
파이널 프로젝트
더 구조화된 접근 방식을 통해 복잡한 라우트를 관리하면서도 React Router
의 최신 기능을 활용하여 애플리케이션의 유지보수성을 향상시킨다.
이렇게 보면 lazy loading 을 무조건 쓰는 것이 최적화에 도움이 되고 안 쓸 이유가 없을 것 같은데 단점이 없는것이 아닌가? 궁금해서 단점에 대해서 한번 찾아보았다.
지연 시간 발생
사용자가 특정 기능이나 페이지에 접근할 때 그 시점에서야 필요한 리소스를 로드하기 시작하므로, 사용자는 해당 컴포넌트가 로드될 때까지 기다려야 할 수 있다.
코드 복잡성 증가
코드 스플리팅과 레이지 로딩을 구현하기 위해서는 추가적인 로직이 필요하며, 이로 인해 코드의 복잡성이 증가하며 개발과 유지 보수가 어려워질 수 있다.
SEO 영향
검색 엔진 최적화에 부정적 영향을 줄 수 있다. 검색 엔진 크롤러가 자바스크립트를 완전히 실행하지 않고 페이지를 크롤링할 경우, 로드되지 않은 컨텐츠는 인덱싱되지 않을 수 있다.
이렇게 단점들을 찾아보았는데, 이렇게 보면 사실 큰 단점이 없는 것 같다.
지연 시간 발생은 어차피 처음에 모든 스크립트들을 로드하는 것도 마찬가지일 것 같고,
코드 복잡성 증가는 사실 개발자 입장에서만 그렇지, 사용자들은 더 나은 경험을 할 것이다.
SEO 영향은 React Helmet
을 씌워도 안되는지 사실 잘 모르겠다. 해봐야 알 것 같다. 사실 그냥 리액트의 문제 아닌가?
그럼 최적의 사용 시나리오는 어떤 경우일까 궁금해졌다. 찾아보니 다음과 같았다.
대형 에플리케이션
많은 페이지와 기능을 가진 대형 애플리케이션에서는 사용자가 접근할 가능성이 낮은 부분까지 모두 초기에 로드하는 것은 비효율적이다. 이런 경우 lazy loading
을 통해 필요한 부분만 로드하도록 설정하는 것이 좋다.
특정 기능이 중요도가 낮은 경우
예를 들어, 애플리케이션의 특정이 기능이 일부 사용자에게만 필요하다면, 그 기능의 로딩을 지연시키고 기본 기능에 더 빠르게 접근할 수 있도록 할 수 있다.
대형 애플리케이션에 적용한다는 거는 조금만 생각하면 금방 알 수 있었던 이유다. 하지만 중요도는 생각치도 못했다. 중요도가 낮은 기능은 스크립트 로드시간을 초기에 하지말고 그 기능이 쓰일 때 로드될 때 하면 기본적인 기능의 접근은 더 빠르게 될 것이다.
작은 프로젝트 때는 굳이 lazy
를 써야할까 싶었는데, 전부 적용하는 것이 아닌 중요도가 낮은 기능에는 충분히 적용할만 한 것 같다.
코드 스플리팅은 웹 애플리케이션의 초기 로딩 시간을 줄이고 사용자 경험을 개선하기 위해 전체 애플리케이션을 작은 청크로 나누어 필요할 때 로드할 수 있게 하는 기법이다. 모듈화는 ES6 이후 자바스크립트에서 가능해졌으며, export
와 import
를 통해 코드를 모듈 간에 공유할 수 있다. 정적 import
는 코드가 항상 로드되지만, 동적 import
는 코드가 실제 필요할 때만 로드되도록 하여 코드 스플리팅을 가능하게 한다.
코드 스플리팅의 이점으로는 성능 최적화, 효율적인 자원 관리, 유지 관리의 용이성 등이 있다. 하지만 단점으로는 지연 시간 발생, 코드 복잡성 증가, SEO에 대한 부정적 영향 등이 있다. 이 기법은 특히 대형 애플리케이션에서 효과적이며, 사용자가 자주 방문하지 않는 페이지나 중요도가 낮은 기능에 적용하면 초기 로드 시간을 줄이면서도 필요한 기능은 적시에 제공할 수 있다.
결론적으로, 코드 스플리팅은 애플리케이션의 성능과 사용자 경험을 향상시키는 중요한 전략이지만, 적용할 때는 애플리케이션의 구조와 사용자의 접근 패턴을 고려해 신중하게 결정해야 한다.