🚀 React에서 공지사항(Notice) 페이징 최적화 & 성능 개선하기
2025. 2. 15. 18:17ㆍ프론트엔드/REACT
728x90
🚀 React에서 공지사항(Notice) 페이징 최적화 & 성능 개선하기
공지사항을 구현할 때, 현재 공지사항 이후의 ID를 찾고, 다음 공지사항을 요청하는 방식을 사용했어요.
하지만 기존 방식대로 id+1, id+2 방식으로 가져오는 것은 문제가 발생할 수 있음 🤔
📌 문제점: id+1, id+2 방식의 한계
- 공지사항 ID가 연속되지 않을 가능성
- id+1이 존재하지 않을 수도 있고, id+2가 건너뛸 수도 있음
- 예를 들어, idArray = [6, 8, 10]이면 id+1(7)이 존재하지 않아서 요청이 실패함
- 불필요한 API 요청
- 만약 마지막 공지사항이라면 id+1, id+2 요청 자체가 필요 없음
- 하지만 기존 방식은 마지막인지 확인하지 않고 항상 2개 요청
✅ 해결 방법: idArray를 활용한 최적화
🔹 1. localStorage에 공지사항 ID 배열(idArray) 저장
- 공지사항 목록을 가져올 때 모든 ID를 idArray로 저장
- 예: idArray = [6, 8, 10, 15, 20]
🔹 2. idArray에서 현재 ID의 다음 ID 찾기
- indexOf() 대신 Map을 사용해 빠르게 다음 ID 탐색 (O(1))
- 마지막 ID라면 요청하지 않음
🔹 3. Promise.all()을 제거하고 요청 순차 처리
- id+1 요청이 실패하면 id+2 요청도 무의미하므로 첫 번째 요청 성공 후 두 번째 요청 실행
🔥 최적화 코드
1️⃣ Answer.js: idArray 저장하기
import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import axiosCookie from '../../axiosCookie';
export const Answer = () => {
const navigate = useNavigate();
const [notices, setNotices] = useState([]);
useEffect(() => {
fetchNotices();
}, []);
// ✅ 공지사항 목록 가져와서 `idArray` 저장
const fetchNotices = async () => {
try {
const response = await axiosCookie.get('/api/notice');
if (response.data?.data?.noticeList) {
const formattedNotices = response.data.data.noticeList.map(notice => ({
...notice,
date: formatDate(notice.createdAt),
}));
setNotices(formattedNotices);
// ✅ `idArray` 저장 (공지사항 ID만 저장)
const idArray = formattedNotices.map(notice => notice.id);
localStorage.setItem('noticeIds', JSON.stringify(idArray));
}
} catch (error) {
console.error('❌ 공지사항 데이터를 불러오는 중 오류 발생:', error);
}
};
const formatDate = (isoString) => {
return new Date(isoString).toISOString().split('T')[0].replace(/-/g, '.');
};
return (
<div>
{notices.map(notice => (
<div key={notice.id} onClick={() => navigate(`/notice/${notice.id}`)}>
<span>{notice.title}</span>
<span>{notice.date}</span>
</div>
))}
</div>
);
};
2️⃣ NoticeDetail.js: idArray 기반으로 다음 ID 요청
import React, { useState, useEffect } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import axiosCookie from '../../axiosCookie';
const NoticeDetail = () => {
const navigate = useNavigate();
const { id } = useParams();
const [notice, setNotice] = useState(null);
const [nextNotices, setNextNotices] = useState([]);
const [idArray, setIdArray] = useState([]);
const [idMap, setIdMap] = useState(new Map());
useEffect(() => {
// ✅ `localStorage`에서 `idArray` 가져와서 `useState`에 저장
const storedIds = JSON.parse(localStorage.getItem('noticeIds')) || [];
setIdArray(storedIds);
// ✅ `idMap`을 생성해 O(1) 탐색 가능하도록 변환
const map = new Map(storedIds.map((id, index) => [id, index]));
setIdMap(map);
}, []);
useEffect(() => {
if (id && idArray.length > 0 && idMap.size > 0) {
fetchNoticeById(Number(id));
}
}, [id, idArray, idMap]);
// ✅ 현재 공지사항 가져오기 + 다음 공지사항 찾기
const fetchNoticeById = async (noticeId) => {
try {
// ✅ 현재 공지사항 가져오기
const currentResponse = await axiosCookie.get(`/api/notice/${noticeId}`);
if (currentResponse.data?.data) {
setNotice({
title: currentResponse.data.data.title,
date: formatDate(currentResponse.data.data.createdAt),
content: currentResponse.data.data.content,
});
}
// ✅ `idArray`에서 현재 ID의 다음 ID 찾기
const currentIndex = idMap.get(noticeId);
const nextNoticesData = [];
if (idArray[currentIndex + 1]) {
const nextResponse = await axiosCookie.get(`/api/notice/${idArray[currentIndex + 1]}`);
nextNoticesData.push({
id: nextResponse.data.data.id,
title: nextResponse.data.data.title,
date: formatDate(nextResponse.data.data.createdAt),
});
}
if (idArray[currentIndex + 2]) {
const nextResponse = await axiosCookie.get(`/api/notice/${idArray[currentIndex + 2]}`);
nextNoticesData.push({
id: nextResponse.data.data.id,
title: nextResponse.data.data.title,
date: formatDate(nextResponse.data.data.createdAt),
});
}
setNextNotices(nextNoticesData);
} catch (error) {
console.error('❌ 공지사항 데이터를 불러오는 중 오류 발생:', error);
}
};
const formatDate = (isoString) => {
return new Date(isoString).toISOString().split('T')[0].replace(/-/g, '.');
};
return (
<div>
<h2>{notice?.title}</h2>
<p>{notice?.date}</p>
<p>{notice?.content}</p>
<div>
{nextNotices.length > 0 ? (
nextNotices.map((item, index) => (
<div key={index} onClick={() => navigate(`/notice/${item.id}`)}>
<span>{item.title}</span>
<span>{item.date}</span>
</div>
))
) : (
<p>다음 공지사항이 없습니다.</p>
)}
</div>
</div>
);
};
export default NoticeDetail;
🎯 최적화 정리
문제 기존 방식 (id+1, id+2) 최적화된 방식 (idArray 활용)
| 연속된 ID 문제 | id+1이 없으면 요청 실패 | idArray에서 정확한 다음 ID 찾음 |
| 불필요한 요청 | 항상 2개 요청 | 마지막이면 요청 안 함 |
| 탐색 속도 | indexOf() (O(n)) | Map 사용 (O(1)) |
| API 요청 | Promise.all() 동시 요청 | 첫 번째 성공 후 두 번째 요청 |
🚀 결론: 성능 최적화 성공!
- ✅ id+1 방식 대신 idArray에서 정확한 다음 ID 찾기
- ✅ localStorage 접근 최소화하여 렌더링 성능 개선
- ✅ idMap을 사용해 탐색 속도를 O(1)으로 최적화
- ✅ 불필요한 API 요청 제거로 네트워크 비용 절감
728x90
'프론트엔드 > REACT' 카테고리의 다른 글
| React 관심사 선택 모달 개선하기 (3) | 2025.02.17 |
|---|---|
| React 모달에서 입력값을 "수정하기" 버튼을 눌러야 반영되도록 하기 (3) | 2025.02.17 |
| React에서 관심사(취미) 선택 모달 구현하기 (6) | 2025.02.16 |
| 📌 React에서 페이지네이션과 유효성 검사 구현하기 (0) | 2025.02.15 |
| 🚀 React Query를 사용하여 FAQ 관리 성능 개선하기 (0) | 2025.02.15 |