fix(travel): useTravelData AbortController 및 에러 핸들링 보완
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -73,6 +73,7 @@ const useTravelData = () => {
|
|||||||
const currentAlbumRef = useRef(null); // { regionId, albumName }
|
const currentAlbumRef = useRef(null); // { regionId, albumName }
|
||||||
const cacheRef = useRef(new Map()); // photo data cache key: `${regionId}::${albumName}`
|
const cacheRef = useRef(new Map()); // photo data cache key: `${regionId}::${albumName}`
|
||||||
const albumCacheRef = useRef(new Map()); // album metadata cache key: regionId
|
const albumCacheRef = useRef(new Map()); // album metadata cache key: regionId
|
||||||
|
const loadAbortRef = useRef(null); // AbortController for loadAlbumPhotos
|
||||||
|
|
||||||
/* ── Load GeoJSON regions once ──────────── */
|
/* ── Load GeoJSON regions once ──────────── */
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -195,7 +196,10 @@ const useTravelData = () => {
|
|||||||
setHasNext(false);
|
setHasNext(false);
|
||||||
pageRef.current = 1;
|
pageRef.current = 1;
|
||||||
|
|
||||||
|
// Abort any in-flight loadAlbumPhotos request
|
||||||
|
if (loadAbortRef.current) loadAbortRef.current.abort();
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
|
loadAbortRef.current = controller;
|
||||||
try {
|
try {
|
||||||
let url = `/api/travel/photos?region=${encodeURIComponent(regionId)}&page=1&size=${PAGE_SIZE}`;
|
let url = `/api/travel/photos?region=${encodeURIComponent(regionId)}&page=1&size=${PAGE_SIZE}`;
|
||||||
if (albumName) url += `&album=${encodeURIComponent(albumName)}`;
|
if (albumName) url += `&album=${encodeURIComponent(albumName)}`;
|
||||||
@@ -241,11 +245,12 @@ const useTravelData = () => {
|
|||||||
setLoadingMore(true);
|
setLoadingMore(true);
|
||||||
setError('');
|
setError('');
|
||||||
|
|
||||||
|
const moreController = new AbortController();
|
||||||
try {
|
try {
|
||||||
let url = `/api/travel/photos?region=${encodeURIComponent(activeRegion)}&page=${pageRef.current}&size=${PAGE_SIZE}`;
|
let url = `/api/travel/photos?region=${encodeURIComponent(activeRegion)}&page=${pageRef.current}&size=${PAGE_SIZE}`;
|
||||||
if (activeAlbum) url += `&album=${encodeURIComponent(activeAlbum)}`;
|
if (activeAlbum) url += `&album=${encodeURIComponent(activeAlbum)}`;
|
||||||
|
|
||||||
const res = await fetch(url);
|
const res = await fetch(url, { signal: moreController.signal });
|
||||||
if (!res.ok) throw new Error(`사진 로딩 실패 (${res.status})`);
|
if (!res.ok) throw new Error(`사진 로딩 실패 (${res.status})`);
|
||||||
const json = await res.json();
|
const json = await res.json();
|
||||||
const { normalized, hasNext: hn, summary } = parsePhotoResponse(json);
|
const { normalized, hasNext: hn, summary } = parsePhotoResponse(json);
|
||||||
@@ -289,26 +294,32 @@ const useTravelData = () => {
|
|||||||
let url = `/api/travel/photos?region=${encodeURIComponent(activeRegion)}&page=1&size=${PAGE_SIZE}`;
|
let url = `/api/travel/photos?region=${encodeURIComponent(activeRegion)}&page=1&size=${PAGE_SIZE}`;
|
||||||
if (activeAlbum) url += `&album=${encodeURIComponent(activeAlbum)}`;
|
if (activeAlbum) url += `&album=${encodeURIComponent(activeAlbum)}`;
|
||||||
|
|
||||||
const res = await fetch(url);
|
const reloadController = new AbortController();
|
||||||
if (!res.ok) throw new Error(`사진 로딩 실패 (${res.status})`);
|
try {
|
||||||
const json = await res.json();
|
const res = await fetch(url, { signal: reloadController.signal });
|
||||||
const { normalized, hasNext: hn, summary } = parsePhotoResponse(json);
|
if (!res.ok) throw new Error(`사진 로딩 실패 (${res.status})`);
|
||||||
|
const json = await res.json();
|
||||||
|
const { normalized, hasNext: hn, summary } = parsePhotoResponse(json);
|
||||||
|
|
||||||
const filtered = activeAlbum
|
const filtered = activeAlbum
|
||||||
? normalized.filter((p) => !p.album || p.album === activeAlbum)
|
? normalized.filter((p) => !p.album || p.album === activeAlbum)
|
||||||
: normalized;
|
: normalized;
|
||||||
|
|
||||||
pageRef.current = 2;
|
pageRef.current = 2;
|
||||||
setPhotos(filtered);
|
setPhotos(filtered);
|
||||||
setHasNext(hn);
|
setHasNext(hn);
|
||||||
cacheRef.current.set(cacheKey, {
|
cacheRef.current.set(cacheKey, {
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
items: filtered,
|
items: filtered,
|
||||||
page: 2,
|
page: 2,
|
||||||
hasNext: hn,
|
hasNext: hn,
|
||||||
summary,
|
summary,
|
||||||
});
|
});
|
||||||
if (summary) setPhotoSummary(summary);
|
if (summary) setPhotoSummary(summary);
|
||||||
|
} catch (err) {
|
||||||
|
if (err?.name === 'AbortError') return;
|
||||||
|
setError(err?.message ?? String(err));
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
/* ── getFilteredAlbums — filter by region ─ */
|
/* ── getFilteredAlbums — filter by region ─ */
|
||||||
|
|||||||
Reference in New Issue
Block a user