API 호출 최소화를 통한 성능 개선!
기능 구현을 위해 구현에만 초점을 맞춰서 만들다가, 성능 측면에서 더 좋은 방법을 조언받아 개선해 보았습니다.
수정 전
수정 후 로직
개선 내용
-> 수정 전에는 댓글 생성 시마다 SELECT API를 호출하여 새로운 댓글을 보여 주었다.
수정 후에는 useState에 댓글 정보를 담아서 임시로 보여 주는 형태로, API 호출을 한 번 줄일 수 있게 됐다.
수정 전 코드
// 새로운 댓글 등록하기 함수(댓글 추출 -> API 실행 -> DB에 댓글 저장)
const handleSubmit = (event: React.MouseEvent<HTMLButtonElement>) => {
const data = insertText.current?.value;
event.preventDefault();
const axiosData = {
CO_CONTEXT: data,
S_IDX,
};
try {
const result = axiosInstance.post("/clubDetail/insertContext", axiosData);
} catch (error) {
console.log(error);
}
if (insertText.current) {
insertText.current.value = "";
}
};
// 새로운 댓글이 등록될 때마다 getContext 함수가 실행되며 리렌더링된다.
useEffect(() => {
getContext(); // 댓글 불러오기 함수(SELECT 댓글)
}, [handleSubmit]);
// JSX 코드
return (
// 댓글 입력 박스
<div className="">
<div className="my-2">
<form>
<div className="flex flex-start">
<input
type="text"
placeholder="댓글을 입력해 주세요"
className="w-4/5 ml-1 border-y-neutral-800"
ref={insertText}
/>
<button
type="button"
className="mx-1 border-2 px-2 rounded-xl"
onClick={handleSubmit}
>
등록
</button>
</div>
</form>
</div>
<div className="overflow-y-auto h-[10rem]">
// 댓글 불러오기 박스
{context?.map((item, index) => (
<div
key={index}
className="flex flex-start border-2 py-2 pl-3 my-1 text-[14px] border-y-gray-200 border-x-slate-100"
>
<p>{item.U_NAME} : </p>
<p> {item.CO_CONTEXT}</p>
</div>
))}
</div>
</div>
);
-> INSERT API 실행 후 SELECT API 실행으로 두 번의 API 호출이 이루어진다.
수정 후 코드
// 댓글 추출용 useRef
const insertText = useRef<HTMLInputElement>(null);
// 댓글 입력 함수
const handleSubmit = async (event: React.MouseEvent<HTMLButtonElement>) => {
// 댓글 추출
const data = insertText.current?.value;
event.preventDefault();
// INSERT API에 보낼 데이터
const axiosData = {
CO_CONTEXT: data,
S_IDX,
};
try {
// INSERT API 실행
const result = await axiosInstance.post(
"/clubDetail/insertContext",
axiosData
);
// 유저 ID와 받아온 INSERT_ID 저장
const newAddComment = { S_IDX: S_IDX.S_IDX, data, id: result.data.id };
// SELECT API 재호출이 아닌, 임시로 보여 줄 댓글 박스 데이터 저장
setTemporaryContext((prevComment) => [...prevComment, newAddComment]);
} catch (error) {
console.log(error);
}
if (insertText.current) {
insertText.current.value = "";
}
};
// 댓글 입력이 완료될 때마다 임시 댓글창을 보여 주기 위해 리렌더링 발생
useEffect(() => {}, [temporaryContext]);
// JSX 코드
<div className="my-2">
<form>
<div className="flex border-2 border-x-white border-t-white flex-start">
<input
type="text"
placeholder="댓글을 입력해 주세요"
className="w-full outline-none ml-1 border-y-neutral-800 pl-2 py-1"
ref={insertText}
/>
<button
type="button"
className="mx-1 px-2 rounded-xl"
onClick={handleSubmit}
>
<MdSend />
</button>
</div>
</form>
</div>
<div>
<div>
// 새로운 API 호출을 통한 댓글 불러오기가 아닌, 리렌더링만으로 댓글을 임시로 보여 주기 위한 코드
{temporaryContext.map((item, index) => (
<div
key={index}
ref={(ref) => (contextRef.current[String(item.id)] = ref)}
>
<div
className="flex justify-between border-2 py-2 pl-3 my-1
text-[14px] border-y-gray-100 border-x-white border-t-white"
>
<p>
{userName} : {item.data}
</p>
<p>
<button
onClick={() => deleteTemporaryContext(Number(item.id))}
>
<GrFormClose />
</button>
</p>
</div>
</div>
))}
</div>
</div>
-> INSERT API 실행 후 정보를 useState에 담아 보여 줌으로써 백엔드와 한 번 더 통신하는 것이 아니라 클라이언트에서 처리할 수 있게 됐다.
(다른 페이지에 갔다가 다시 들어오면 useState는 초기화되고 SELECT API를 통해 댓글을 불러온다.)
사용자가 많은 서비스에서는 API 호출 한 번이 성능에 영향을 줄 수 있다.
그래서 기능을 구현할 때 렌더링을 최소화하고, API를 호출하지 않고 클라이언트에서 처리할 수 있는 방법을 고민할 필요가 있다.
React 환경에서 작업할 때는 렌더링을 어떻게 최소화할 수 있을지에 대해서만 생각하며 만들어 왔어서, API 호출을 줄이는 방향은 생각하지 못했었습니다. 앞으로는 기능을 구현할 때 조금 더 나은 방향을 생각해 보고 구현할 수 있을 것 같습니다.