Graphy | Frontend Issue : #2 Apply Lazy Loading

mineii
6 min readJun 10, 2023

--

기본적으로 React 애플리케이션이 처음 로딩될 때에는 모든 컴포넌트 코드가 로딩된다. 사용자가 메인페이지만 보고 싶다고 해도 다른 모든 페이지가 전부 로딩이 되야 볼 수 있는 것이다. 그래서 React는 자연스럽게 첫 로딩시간이 오래 걸리게 된다. 이런 문제를 해결하기 위한 방법 중에는 “Lazy Loading”이라는 기법이 있다.

Lazy Loading은 컴포넌트를 처음부터 로딩하지 않고, 그 컴포넌트가 실제로 필요할 때까지 로딩을 미루는 방식이다. React에서는 React.lazy() 함수와 Suspense 컴포넌트를 함께 사용하여 Lazy Loading을 구현할 수 있다.

import React, { Suspense } from 'react';

const Component = React.lazy(() => import('./Component'));

function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<Component />
</Suspense>
</div>
);
}

하지만 Lazy Loading을 적용하고 나니 기존의 오프라인 기능에 문제가 생겼다. 적용하기 이전에는 사용자가 온라인 상태에서 한 번이라도 애플리케이션에 접속하면, 모든 페이지가 캐싱되었다. 이후에는 오프라인 상태에서도 캐싱된 페이지들을 자유롭게 이용할 수 있었다.

그러나 Lazy Loading을 적용한 후에는 사용자가 접근한 페이지만 로드하기 때문에, 사용자가 접근한 페이지만 캐싱이 되었다. 따라서 사용자가 오프라인 상태에서 이전에 방문하지 않았던 페이지에 접근하려고 하면, 해당 페이지는 캐싱되지 않았기 때문에 접근에 문제가 생겼다.

이에 해결 방법을 찾던 중 서비스 워커와 메시징을 활용하면 사용자가 페이지에 접근하기 전에 해당 페이지가 캐싱되어 있는지 확인할 수 있다는 것을 알게 되었다. 그 해결 방법은 다음과 같다.

  if (
!navigator.onLine &&
'serviceWorker' in navigator &&
navigator.serviceWorker.controller
) {
const messageChannel = new MessageChannel();

브라우저가 오프라인 상태이고 서비스 워커가 설치되어 있을 경우, MessageChannel을 생성하여 서비스 워커와 메시지를 주고 받는다.

MessageChannel 인터페이스는 두 개의 채널을 제공하는데, 각각을 port1port2라고 부른다. 이 두 포트는 서로 양방향 통신을 할 수 있는 메시지 채널을 형성한다.

   Promise.resolve().then(() => {
if (navigator.serviceWorker.controller) {
navigator.serviceWorker.controller.postMessage(
{ action: 'cache-contains', url: urlToCheck },
[messageChannel.port2],
);
}
});
  • port2: 이 포트는 보통 메시지를 전송하는데 사용된다. 위에서는 서비스 워커로 메시지를 보내기 위해 navigator.serviceWorker.controller.postMessage 함수의 두 번째 매개변수로 전달된다.
  • 참고로 비동기 코드의 특성 상, 첫 번째 if (navigator.serviceWorker.controller) 검사와 Promise.resolve().then() 내의 검사 사이에 시간적인 간격이 있어, 다시 한번 navigator.serviceWorker.controller를 검사하였다.
   messageChannel.port1.onmessage = (event) => {
if (event.data.hasMatch) {
navigate(`url`);
} else {
alert('오프라인 상태입니다. 네트워크 연결을 확인해주세요.');
}
};
}
  • port1: 이 포트는 보통 메시지를 수신하는 데 사용된다. messageChannel.port1.onmessage 콜백 함수는 서비스 워커로부터 메시지를 받아 해당 URL의 캐시 존재 여부를 확인하고, 캐시가 존재한다면 해당 페이지로 이동한다. 그렇지 않다면, 사용자에게 오프라인 상태임을 알리는 알림을 표시한다.
캐시가 존재한다면 접근이 가능하지만 존재하지 않는다면 불가능하다.

전체 코드

// 서비스 워커 코드
self.addEventListener('message', (event) => {
if (event.data.action === 'cache-contains') {
caches
.open(CACHE_NAME)
.then((cache) => cache.match(event.data.url))
.then((match) => {
event.ports[0].postMessage({ hasMatch: !!match });
});
}
});
// 페이지 접근 함수
function toURL() {
const urlToCheck = `url`;

if (
!navigator.onLine &&
'serviceWorker' in navigator &&
navigator.serviceWorker.controller
) {
const messageChannel = new MessageChannel();

messageChannel.port1.onmessage = (event) => {
if (event.data.hasMatch) {
navigate(`url`);
} else {
alert('오프라인 상태입니다. 네트워크 연결을 확인해주세요.');
}
};

Promise.resolve().then(() => {
if (navigator.serviceWorker.controller) {
navigator.serviceWorker.controller.postMessage(
{ action: 'cache-contains', url: urlToCheck },
[messageChannel.port2],
);
}
});
} else {
navigate(`url`);
}
}

--

--