웹 지도 애플리케이션에서 지도의 로딩 속도를 개선하고 네트워크 트래픽을 줄이기 위해 한 번 불러온 타일 데이터를 브라우저에 캐싱하는 것은 매우 효과적입니다. OpenLayers에서는 setTileLoadFunction을 사용하여 타일을 불러오는 로직을 직접 제어할 수 있습니다. localforage 라이브러리를 활용해 IndexedDB에 타일 데이터를 저장하고 관리하는 방법을 살펴보겠습니다.
1. 의존성 설치
먼저 브라우저 저장소를 편리하게 다루기 위해 localforage를 프로젝트에 설치합니다.
npm install localforage
2. 타일 캐시 관리 모듈 작성
IndexedDB를 초기화하고 타일을 조회하거나 저장하는 로직을 별도의 함수로 구성합니다. 아래 코드는 타일의 URL을 키로 사용하여 Blob 데이터를 저장합니다.
import localforage from 'localforage';
// IndexedDB 인스턴스 초기화
const tileDB = localforage.createInstance({
name: 'MapTileCache',
driver: localforage.INDEXEDDB,
storeName: 'tiles'
});
const MAX_RETRIES = 3;
const retryTracker = {};
/**
* 타일 로드 함수 정의
*/
const customTileLoader = (tile, src) => {
const imageElement = tile.getImage();
// 1. 캐시 확인
tileDB.getItem(src).then((cachedBlob) => {
if (cachedBlob) {
// 캐시 적중 시 Blob URL 생성 후 적용
const blobUrl = URL.createObjectURL(cachedBlob);
imageElement.src = blobUrl;
// 메모리 누수 방지를 위한 해제 처리 (필요에 따라 조절)
imageElement.onload = () => URL.revokeObjectURL(blobUrl);
return;
}
// 2. 캐시 부재 시 서버에서 요청
fetchTileFromServer(tile, src, imageElement);
}).catch((err) => {
console.error('캐시 로드 실패:', err);
fetchTileFromServer(tile, src, imageElement);
});
};
/**
* 네트워크를 통해 타일을 가져오고 캐시에 저장
*/
function fetchTileFromServer(tile, src, img) {
fetch(src, {
method: 'GET',
mode: 'cors'
})
.then((response) => {
if (!response.ok) {
// 400, 500 계열 에러 시 재시도 로직
if ([400, 404, 500, 502, 503, 504].includes(response.status)) {
handleRetry(tile, src);
}
throw new Error('네트워크 응답 오류');
}
return response.blob();
})
.then((blob) => {
const objectUrl = URL.createObjectURL(blob);
img.src = objectUrl;
// 신규 타일 캐시 저장
tileDB.setItem(src, blob);
})
.catch(() => {
tile.setState(3); // 에러 상태 전이
});
}
/**
* 지연 로딩을 통한 재시도 처리
*/
function handleRetry(tile, src) {
retryTracker[src] = (retryTracker[src] || 0) + 1;
if (retryTracker[src] <= MAX_RETRIES) {
const delay = retryTracker[src] * 300;
setTimeout(() => tile.load(), delay);
}
}
export default customTileLoader;
3. OpenLayers 레이어에 적용하기
정의한 타일 로드 함수를 OpenLayers의 소스 객체에 적용합니다. XYZ 방식뿐만 아니라 WMTS 등 다양한 타일 소스에 동일한 방식을 적용할 수 있습니다.
import TileLayer from 'ol/layer/Tile';
import XYZ from 'ol/source/XYZ';
import customTileLoader from './tileCacheStore';
const mapLayer = new TileLayer({
source: new XYZ({
url: 'https://api.example.com/tiles/{z}/{x}/{y}.png',
crossOrigin: 'anonymous' // CORS 설정 필수
}),
preload: Infinity
});
// 커스텀 타일 로드 함수 할당
mapLayer.getSource().setTileLoadFunction(customTileLoader);
주요 포인트
- CORS 설정:
fetch를 통해 타일을 가져오고 Blob으로 변환하려면 지도 서버에서 CORS가 허용되어야 하며, OpenLayers 소스 설정 시crossOrigin: 'anonymous'옵션이 필요합니다. - 메모리 관리:
URL.createObjectURL로 생성된 URL은 사용이 끝난 후URL.revokeObjectURL을 통해 해제해 주는 것이 메모리 관리에 유리합니다. - IndexedDB: LocalStorage보다 용량 제한이 훨씬 자유로운 IndexedDB를 사용하므로 대량의 타일 이미지를 안정적으로 보관할 수 있습니다.