API 호출 횟수 단축을 통한 성능 개선

성찬홍·2023년 6월 8일
0

Code Refactoring

목록 보기
2/5

API 호출 최소화를 통한 성능 개선!

기능 구현을 위해 , 구현에만 초점을 맞춰서 만들다가 성능 측면에서 더 좋은 방법을 조언받아서 개선해보았습니다.

수정 전/후 로직

수정 전

  1. 페이지 입장시 SELECT API를 실행해서 댓글을 불러와 클라이언트에 보여준다.
  2. 댓글 등록을 위해 , 댓글을 입력하고 버튼을 누른다.
  3. INSERT API 실행을 통해 , MYSQL에 저장한다.
  4. useEffect를 통해 , 댓글 버튼 클릭 시마다 SELECT API를 실행하여, 다시 댓글리스트를 불러온다.
  5. 댓글 등록 완료

수정 후 로직

  1. 페이지 입장 시 SELECT API를 실행해서 댓글을 불러와 클라이언트에 보여준다.
  2. 댓글 등록을 위해 , 댓글을 입력하고 버튼을 누른다.
  3. INSERT API 결과를 통해 방금 입력한 댓글의 MYSQL INSERT_ID를 받아온다.
    (INSERT_ID는 임시 댓글창에도 댓글 삭제 기능을 넣어주기 위함임)
  4. 추출된 댓글과 INSERT_ID를 useState에 저장한다.
  5. useEffect를 이용하여 useState에 저장될때마다 map을 이용하여 , 임시 댓글을 클라이언트에 보내준다.
  6. 댓글 등록 완료

개선 내용
-> 수정전에는 댓글 생성시마다 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>&nbsp; {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} :&nbsp; {item.data}
                </p>
                <p>
                  <button
                    onClick={() => deleteTemporaryContext(Number(item.id))}
                  >
                    <GrFormClose />
                  </button>
                </p>
              </div>
            </div>
          ))}

-> INSERT API 실행 후 정보를 useState에 담아 뿌려줌으로써 백엔드에 한번 더 통신하는 것이 아닌 클라이언트에서 처리할 수 있게 됐다.
( 다른 페이지에 갔다가 다시 들어오면, useState는 초기화되고 SELECT API를 통해 댓글이 불러와진다.)

정리

사용자가 많은 서비스에서는 API 호출 한 번이 성능에 영향을 줄 수 있다.
그래서 기능 구현을 할 때 , 렌더링을 최소화하고 API호출을 하지 않고 클라이언트에서 처리할 수 있는 방법을 고민할 필요가 있다.

느낀점

React 환경에서 작업할 때는 렌더링을 어떻게 최소화할 수 있을까에 대해서만 생각하고 만들어왔어서, api 호출을 줄이는 방향은 생각하지 못했었습니다. 앞으로는 기능 구현할 때, 좀 더 나은 방향으로 생각해보고 구현해볼 수 있을 것 같습니다.

profile
꾸준한 개발자

0개의 댓글