Skip to content
Snippets Groups Projects
Commit e3404f25 authored by pjookim's avatar pjookim
Browse files

test: bottleneck

parent 67bddcab
No related branches found
No related tags found
No related merge requests found
......@@ -14,7 +14,6 @@ const AddPlace = ({ tripId, day, onBack, onPlaceSelect, tripStartDate, tripEndDa
const [searchKeyword, setSearchKeyword] = useState('');
const [favorites, setFavorites] = useState(new Set());
const [favoriteItems, setFavoriteItems] = useState([]);
const [debouncedKeyword, setDebouncedKeyword] = useState(searchKeyword);
const [lastApiCall, setLastApiCall] = useState(0);
const API_CALL_INTERVAL = 1000;
......@@ -22,15 +21,11 @@ const AddPlace = ({ tripId, day, onBack, onPlaceSelect, tripStartDate, tripEndDa
attractions: {
places: [],
loading: false,
pageNo: 1,
hasMore: true,
totalCount: 0
},
festivals: {
places: [],
loading: false,
pageNo: 1,
hasMore: true,
totalCount: 0
}
});
......@@ -61,46 +56,21 @@ const AddPlace = ({ tripId, day, onBack, onPlaceSelect, tripStartDate, tripEndDa
};
useEffect(() => {
const currentState = tabStates[activeTab];
if (currentState.pageNo > 1 && currentState.hasMore) {
searchPlaces(currentState.pageNo, false);
if (tabStates[activeTab].places.length === 0) {
searchPlaces();
}
}, [tabStates[activeTab].pageNo]);
}, [activeTab]);
const handleTabChange = (newTab) => {
setActiveTab(newTab);
if (tabStates[newTab].places.length === 0) {
searchPlaces(1, true);
}
};
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedKeyword(searchKeyword);
}, 300);
return () => clearTimeout(handler);
}, [searchKeyword]);
useEffect(() => {
const currentState = tabStates[activeTab];
console.log('페이지 번호:', currentState.pageNo);
console.log('로딩 상태:', currentState.loading);
console.log('더 불러올 데이터 여부:', currentState.hasMore);
console.log('현재 탭:', activeTab);
console.log('현재 탭의 데이터 개수:', currentState.places.length);
}, [tabStates, activeTab]);
useEffect(() => {
updateTabState(activeTab, {
pageNo: 1,
hasMore: true
});
if (tabStates[activeTab].places.length === 0) {
searchPlaces(1, true);
}
}, [activeTab]);
useEffect(() => {
const fetchFavorites = async () => {
if (!user) return;
......@@ -142,9 +112,6 @@ const AddPlace = ({ tripId, day, onBack, onPlaceSelect, tripStartDate, tripEndDa
fetchFavorites();
}, [user]);
const observerRef = useRef();
const loadingRef = useRef(null);
const TOUR_API_KEY = process.env.REACT_APP_OPEN_API_KEY;
const TOUR_API_BASE_URL = 'https://apis.data.go.kr/B551011/KorService1';
......@@ -243,70 +210,6 @@ const AddPlace = ({ tripId, day, onBack, onPlaceSelect, tripStartDate, tripEndDa
}
};
const observerCallback = useCallback(entries => {
const target = entries[0];
const currentState = tabStates[activeTab];
if (target.isIntersecting && currentState.hasMore && !currentState.loading) {
const now = Date.now();
const timeToWait = API_CALL_INTERVAL - (now - lastApiCall);
if (timeToWait <= 0) {
updateTabState(activeTab, {
pageNo: currentState.pageNo + 1
});
setLastApiCall(now);
console.log('API 호출 준비 완료');
} else {
console.log(`API 호출 간격이 너무 짧습니다. ${timeToWait}ms 후 재시도...`);
setTimeout(() => {
updateTabState(activeTab, {
pageNo: currentState.pageNo + 1
});
setLastApiCall(Date.now());
console.log('대기 후 API 호출 준비 완료');
}, timeToWait);
}
}
}, [tabStates, activeTab, lastApiCall]);
useEffect(() => {
const observer = new IntersectionObserver(observerCallback, {
threshold: 0.5,
rootMargin: '100px'
});
observerRef.current = observer;
const loadingElement = loadingRef.current;
if (loadingElement) {
observer.observe(loadingElement);
}
return () => observer.disconnect();
}, [observerCallback, tabStates[activeTab].loading]);
useEffect(() => {
const loadingElement = loadingRef.current;
const currentState = tabStates[activeTab];
if (loadingElement && observerRef.current) {
observerRef.current.observe(loadingElement);
}
return () => {
if (loadingElement && observerRef.current) {
observerRef.current.unobserve(loadingElement);
}
};
}, [activeTab, tabStates[activeTab].loading]);
useEffect(() => {
const currentState = tabStates[activeTab];
if (currentState.pageNo > 1 && currentState.hasMore) {
searchPlaces(currentState.pageNo, false);
}
}, [tabStates[activeTab].pageNo]);
const formatISODate = (isoDateString) => {
const date = new Date(isoDateString);
const year = date.getFullYear();
......@@ -315,9 +218,9 @@ const AddPlace = ({ tripId, day, onBack, onPlaceSelect, tripStartDate, tripEndDa
return `${year}-${month}-${day}`;
};
const searchPlaces = async (page, isNewSearch, keyword = '') => {
const searchPlaces = async (keyword = '') => {
const currentState = tabStates[activeTab];
if (currentState.loading || (!currentState.hasMore && !isNewSearch)) return;
if (currentState.loading) return;
const formatDate = (dateString) => {
if (!dateString) return null;
......@@ -343,14 +246,6 @@ const AddPlace = ({ tripId, day, onBack, onPlaceSelect, tripStartDate, tripEndDa
const formattedTripStartDate = formatISODate(tripStartDate).replace(/-/g, '');
const formattedTripEndDate = formatISODate(tripEndDate).replace(/-/g, '');
console.log('축제 검색 파라미터:', {
시작일: formattedTripStartDate,
종료일: formattedTripEndDate,
지역코드: areaCode,
페이지: page,
키워드: keyword
});
apiParams = keyword
? `&keyword=${encodeURIComponent(keyword)}`
: `&eventStartDate=${formattedTripStartDate}&eventEndDate=${formattedTripEndDate}`;
......@@ -367,49 +262,37 @@ const AddPlace = ({ tripId, day, onBack, onPlaceSelect, tripStartDate, tripEndDa
`&MobileOS=ETC` +
`&MobileApp=AppTest` +
`&arrange=E` +
`&numOfRows=${CONTENT_TYPE[activeTab] === '15' ? 30 : 20}` +
`&pageNo=${page}` +
`&numOfRows=999` +
`&pageNo=1` +
apiParams +
`&_type=json`;
console.log('API 요청 URL:', apiUrl);
const response = await fetch(apiUrl);
const text = await response.text();
console.log('API 응답 원본:', text);
if (text.includes('<OpenAPI_ServiceResponse>')) {
console.error('API 에러 응답 발생');
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(text, 'text/xml');
const errorMsg = xmlDoc.querySelector('returnAuthMsg')?.textContent;
const reasonCode = xmlDoc.querySelector('returnReasonCode')?.textContent;
let userErrorMsg = '서비스 오류가 발생했습니다. 잠시 후 다시 시도해주세요.';
if (errorMsg === 'LIMITED_NUMBER_OF_SERVICE_REQUESTS_EXCEEDS_ERROR') {
userErrorMsg = '일일 API 호출 한도를 초과습니다. 잠시 후 다시 시도해주세요.';
userErrorMsg = '일일 API 호출 한도를 초과습니다. 잠시 후 다시 시도해주세요.';
}
console.log(userErrorMsg);
updateTabState(activeTab, {
loading: false,
hasMore: false,
error: userErrorMsg
});
return;
}
const data = JSON.parse(text);
console.log('파싱된 API 응답:', data);
const items = data.response.body.items.item || [];
if (activeTab === 'festivals') {
const newPlaces = items.map(item => {
console.log('축제 아이템 데이터:', item);
return {
const newPlaces = items.map(item => ({
id: item.contentid,
type: item.contenttypeid,
name: item.title,
......@@ -425,19 +308,14 @@ const AddPlace = ({ tripId, day, onBack, onPlaceSelect, tripStartDate, tripEndDa
eventEndDate: formatDate(item.eventenddate),
sponsor: item.sponsor1,
usetimefestival: item.usetimefestival
};
});
console.log('변환된 축제 데이터:', newPlaces);
}));
updateTabState('festivals', {
places: isNewSearch ? newPlaces : [...currentState.places, ...newPlaces],
places: newPlaces,
loading: false,
hasMore: newPlaces.length > 0,
totalCount: parseInt(data.response.body.totalCount)
});
} else {
const total = parseInt(data.response.body.totalCount);
const newPlaces = items.map(item => ({
id: item.contentid,
type: item.contenttypeid,
......@@ -453,15 +331,13 @@ const AddPlace = ({ tripId, day, onBack, onPlaceSelect, tripStartDate, tripEndDa
}));
updateTabState('attractions', {
places: isNewSearch ? newPlaces : [...currentState.places, ...newPlaces],
places: newPlaces,
loading: false,
hasMore: newPlaces.length > 0,
totalCount: total
totalCount: parseInt(data.response.body.totalCount)
});
}
} catch (error) {
console.error('Failed to fetch places:', error);
} finally {
updateTabState(activeTab, { loading: false });
}
};
......@@ -511,20 +387,30 @@ const AddPlace = ({ tripId, day, onBack, onPlaceSelect, tripStartDate, tripEndDa
}
};
const handleSearchChange = (e) => {
const keyword = e.target.value.trim();
setSearchKeyword(keyword);
updateTabState(activeTab, {
places: [],
totalCount: 0
});
searchPlaces(keyword);
};
useEffect(() => {
if (debouncedKeyword) {
if (searchKeyword) {
// 검색어가 있을 때는 첫 페이지부터 새로 검색
updateTabState(activeTab, {
places: [],
pageNo: 1,
hasMore: true
totalCount: 0
});
searchPlaces(1, true, debouncedKeyword);
searchPlaces(searchKeyword);
} else if (tabStates[activeTab].places.length === 0) {
// 검색어가 없고 장소 목록이 비어있을 때는 기본 지역 검색
searchPlaces(1, true);
searchPlaces();
}
}, [debouncedKeyword, activeTab]);
}, [searchKeyword, activeTab]);
return (
<div className="add-place">
......@@ -533,7 +419,7 @@ const AddPlace = ({ tripId, day, onBack, onPlaceSelect, tripStartDate, tripEndDa
type="text"
placeholder="장소를 검색하세요"
value={searchKeyword}
onChange={(e) => setSearchKeyword(e.target.value)}
onChange={handleSearchChange}
className="search-input"
/>
</div>
......@@ -612,24 +498,12 @@ const AddPlace = ({ tripId, day, onBack, onPlaceSelect, tripStartDate, tripEndDa
</div>
</div>
))}
<div ref={loadingRef} className="loading-indicator">
{tabStates[activeTab].loading && (
<div>
<div className="loading-indicator">
<div className="spinner" />
<div className="loading-text">로딩 ...</div>
</div>
)}
{!tabStates[activeTab].loading && tabStates[activeTab].hasMore && (
<div className="loading-text">
스크롤하여 보기
</div>
)}
{!tabStates[activeTab].hasMore && (
<div className="loading-text">
이상 결과가 없습니다
</div>
)}
</div>
</div>
</div>
);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment