Front-End - Main Menu/React (Next.js)
[React.js] useEffect로 불필요하게 두 번 이상 실행되어 API가 중복 호출되는 문제 해결하기 (feat. MyApp 프로젝트)
ITRecipe
2025. 3. 14. 11:28
안녕하세요 itrecipe 입니다.
현생이 바빠 오랜만에 포스팅 합니다.
토이 프로젝트를 진행하면서 마주한 문제에 대한
어려움과 해결법을 간략히 남기고자 합니다.
그럼 시작하겠습니다.
[ListContainer.jsx] : 기존 문제 발생 코드
import BoardList from "../../components/board/BoardListForm";
import * as boards from '../../apis/boards'
import { useEffect, useState } from "react";
import { useLocation } from "react-router-dom";
const ListContainer = () => {
// TODO: boardlist state 선언
// state 선언
const [boardList, setBoardList] = useState([])
const [pagination, setPagination] = useState({}) // useState({}) 여기서는 객체로 넘어오기 떄문에 빈객체로 넣어둠
const [page, setPage] = useState(1)
const [size, setSize] = useState(10)
/* 페이지 네이션 초기 코드
?파라미터 = 값 가져오는 법
const location = useLocation()
const query = new URLSearchParams(location.search)
const page = query.get("page") ?? 1 //"page" 파라미터 값을 가져온다. 기본값이 없을 경우 ?? 1 or ?? 10 이런식으로 값을 임의로 세팅하기
const size = query.get("size") ?? 10
*/
// ?파라미터 = 값 가져오는 법
const location = useLocation() // 전역에서 사용할 수 있도록 분리
const updatePage = () => {
const query = new URLSearchParams(location.search)
const newPage = query.get("page") ?? 1
const newSize = query.get("size") ?? 10
console.log(`updatePage() -> newPage : ${newPage}`)
console.log(`updatePage() -> newSize : ${newSize}`)
setPage(newPage)
setSize(newSize)
// getList()
}
// 게시글 목록 데이터 요청 (기존 게시판 코드)
/* const getList = async () => {
const response = await boards.list()
const data = await response.data
const list = data.list
const pagination = data.pagination
// 디버깅 코드 - 2
console.dir('getList() -> data 확인 : ', data)
console.dir('getList() -> data.list 확인 : ', data.list)
console.dir('getList() -> data.pagination 확인 : ', data.pagination)
// 디버깅 코드 - 1
// console.log(`data : ${data}`)
// console.log(`list : ${list}`)
// console.log(`pagination : ${pagination}`)
// TODO: boardList state 업데이트
setBoardList( list )
setPagination( pagination )
}
*/
// 게시글 목록 데이터 요청 (페이지네이션 기능 확장)
const getList = async () => {
const response = await boards.list(page, size)
const data = await response.data
const list = data.list
const pagination = data.pagination
// 디버깅 코드 - 2
console.dir('getList() -> data 확인 : ', data)
console.dir('getList() -> data.list 확인 : ', data.list)
console.dir('getList() -> data.pagination 확인 : ', data.pagination)
// 디버깅 코드 - 1
// console.log(`data : ${data}`)
// console.log(`list : ${list}`)
// console.log(`pagination : ${pagination}`)
// TODO: boardList state 업데이트
setBoardList( list )
setPagination( pagination )
}
useEffect( () => {
updatePage()
}, [location.search])
useEffect( () => {
getList()
}, [page, size])
return (
// <></> 리액트 프래그먼트 문법이자 축약형 문법 : 여러 요소를 하나의 부모로 묶을 수 있도록 해줌 (그룹화)
<>
<div>ListContainer</div>
<BoardList boardList={boardList} pagination={pagination} />
</>
);
};
export default ListContainer;
[ListContainer.jsx] : 개선된 코드
import BoardList from "../../components/board/BoardListForm";
import * as boards from '../../apis/boards';
import { useEffect, useState } from "react";
import { useLocation } from "react-router-dom";
const ListContainer = () => {
const [boardList, setBoardList] = useState([]);
const [pagination, setPagination] = useState({});
const location = useLocation();
useEffect(() => {
const query = new URLSearchParams(location.search);
const newPage = Number(query.get("page")) || 1;
const newSize = Number(query.get("size")) || 10;
console.log(`updatePage() -> newPage: ${newPage}`);
console.log(`updatePage() -> newSize: ${newSize}`);
const getList = async (pageParam, sizeParam) => {
const response = await boards.list(pageParam, sizeParam);
const data = response.data;
console.dir('getList() -> data 확인 : ', data);
setBoardList(data.list);
setPagination(data.pagination);
};
getList(newPage, newSize);
}, [location.search]);
return (
<>
<div>ListContainer</div>
<BoardList boardList={boardList} pagination={pagination} />
</>
);
};
export default ListContainer;
[트러블 슈팅]
- 문제 상황 : useEffect가 불필요하게 두 번 실행되어 API가 중복 호출됨.
- 원인 분석 :
- React의 StrictMode가 개발 환경에서 useEffect를 두 번 실행함.
- page와 size를 useState로 따로 관리하면서, useEffect가 각각 실행됨.
- updatePage()와 getList()가 따로 호출되면서 API가 중복 요청됨.
- 해결 방법 :
- 기존에는 page와 size를 useState로 관리하여 변경될 때마다 useEffect가 실행됨.
- 개선된 코드: location.search에서 즉시 page와 size 값을 추출하여 API를 한 번만 호출하도록 변경.
- useEffect 내부에서 getList()를 호출하여 한 번만 실행되도록 최적화.
- main 컴포넌트에서 사용중인 StrictMode를 끄거나 주석 처리를 하면 중복 호출 방지됨.
- 결과 :
- useEffect 중복 실행이 방지됨.
- API가 한 번만 호출되면서 불필요한 요청 제거.
- 성능 최적화 및 코드 가독성 향상.

