[ React ] 불필요한 리렌더링 없애기!

ljk4268·2023년 12월 20일
0

➕ 토이프로젝트

목록 보기
1/2

불필요한 렌더링 제거 전

  • 마이크 클릭시 버튼 '오늘의 질문'과 '쪽지 남기기'가 렌더링 되고 있어요!

불필요한 렌더링 제거 후

  • 마이크 클릭해도 버튼 '오늘의 질문'과 '쪽지 남기기'가 렌더링 되지 않게 되었어요! ㅎㅎ

최근에는 포텐데이X클로바 스튜디오 프로젝트에 참여했었는데, 그때 만들었던 페이지를 개선하는 작업을 진행 중이에요.

프로젝트가 끝난 후에는 페이지의 성능을 향상시킬 부분을 찾아내고 있는데,
그 중에서 가장 먼저 해결한 부분은 ⚙️불필요한 리렌더링을 줄이거나 없애는 작업이었습니다!

위의 영상에 보이는것처럼 하단에 있는 마이크 버튼을 클릭하면 메시지 창이 열리는데,
위쪽의 버튼들은 상관 없는 부분이라 그 부분에 대해서는 리렌더링이 필요하지 않습니다.
근데 리렌더링이 일어나고 있죠 😅😅😅

이 부분을 수정해보도록 하겠습니다.


✔️ 불필요한 리렌더링이 일어났던 코드

// hook
import useAlertModal from '../../hooks/useAlertModal'

const AMainPage = () => {
  const textArr = ['오늘의 질문', '쪽지 남기기']
  const [action, setAction] = useState('')
  const { openAlertModal, AlertModalComponent } = useAlertModal()

 const handleClickAction = useCallback(
    (text?: string) => {
      if (!text) return
      setAction(text)
      openAlertModal()
    },
    [openAlertModal]
  )
 
 return (
 	<div className="self-stretch flex flex-col items-start justify-start gap-[20px]">
   {textArr.map((text) => {
   	return (
     <SelectionButton
     	key={text}
 		text={text}
  		onClick={handleClickAction}
  		isActive={action === text}
  	/>
    )
	})}
  </div>
	{/* alertModal */}
    {AlertModalComponent}
 )
   
 
}

불필요한 리렌더링을 일으키는 부분의 코드만 가지고 왔습니다.

먼저, 코드 설명을 하자면

1. 버튼

버튼의 경우 textArr라는 배열을 map함수로 반복하면서
'오늘의 질문', '쪽지 남기기'라는 2개의 버튼을 생성합니다.
각각의 버튼들은 handleClickAction라는 메소드를 props로 받습니다.

2. hook함수

'오늘의 질문', '쪽지 남기기'라는 버튼을 누르면 나오는 팝업창입니다.
(프로젝트 기간이 10일이라는 짧은 기간이였기에 핵심기능을 제외한 부가적인 기능은 팝업처리 하기로 되어있었어요! )

해당 팝업은 여러 페이지에서 쓰이기에 저는 hook 함수로 따로 지정을 해두었습니다.

import { useState, useCallback } from 'react'
// components
import AlertModal from '../components/modal/AlertModal'

const useAlertModal = () => {
  const [isModal, setIsModal] = useState(false)

  const openAlertModal = () => {
    setIsModal(!isModal)
  }

  const handleClose = useCallback(() => {
    setIsModal(false)
  }, [])
  const AlertModalComponent = isModal && <AlertModal onClick={handleClose} />

  return { openAlertModal, AlertModalComponent }
}

export default useAlertModal

✔️ 불필요한 리렌더링이 일어났던 이유

'오늘의 질문', '쪽지 남기기'라는 버튼을 누르면 실행되는 handleClickAction() 메소드는
state action을 변경시키는 로직이 들어있습니다.

const handleClickAction = useCallback(
    (text?: string) => {
      if (!text) return
      setAction(text)
      openAlertModal()
    },
    [openAlertModal]
  )

state action을 변경
➡️ AMainPage 컴포넌트 리렌더링
➡️ AMainPage 컴포넌트안에 있는 함수나, 변수들 재생성

물론, useCallback으로 감싼 함수들은 함수가 재생성되지 않을 것이지만, 문제는 handleClickAction() 메소드에 있는 useCallback의 의존성 배열에 포함된 openAlertModal이었습니다.

const AMainPage = () => {
  const { openAlertModal, AlertModalComponent } = useAlertModal()
  
  const handleClickAction = useCallback(
    (text?: string) => {
      if (!text) return
      setAction(text)
      openAlertModal()
    },
    [openAlertModal]
  )
}

AMainPage 컴포넌트가 리렌더링 되면
useAlertModal()도 재실행되면서 그 안에 있는 함수들(openAlertModal, AlertModalComponent)도 재생성이 될거고,
openAlertModal 재생성되면 openAlertModal을 의존성배열로 갖고있는 handleClickAction도 재생성 됩니다.

이 말은 곧,
handleClickAction을 props로 받는 자식 컴포넌트도 리렌더링이 된다는 말이지요!

// '오늘의 질문', '쪽지 남기기'라는 버튼 만드는 부분
{textArr.map((text) => {
   	return (
     <SelectionButton
     	key={text}
 		text={text}
  		onClick={handleClickAction}
  		isActive={action === text}
  	/>
    )
	})}

✔️ 해결 방법은 ?

hook으로 만든 useAlertModal()에서 useCallback을 써주면 됩니다!

import { useState, useCallback } from 'react'
// components
import AlertModal from '../components/modal/AlertModal'

const useAlertModal = () => {
  const [isModal, setIsModal] = useState(false)

  const openAlertModal = useCallback(() => {
    setIsModal(!isModal)
  }, [isModal])

  const handleClose = useCallback(() => {
    setIsModal(false)
  }, [])
  const AlertModalComponent = isModal && <AlertModal onClick={handleClose} />

  return { openAlertModal, AlertModalComponent }
}

export default useAlertModal

useAlertModal()에서 리턴하는 값을 useCallback으로 감싸게 되면
부모컴포넌트가 리렌더링을 해서 useAlertModal()이 실행되더라도
openAlertModal()메소드가 렌더링마다 새로 생성되는 것을 방지할 수 있습니다.

openAlertModal()메소드가 새로 생성되지 않는다면,
openAlertModal를 의존성배열로 가지고 있는 handleClickAction()메소드도 재생성 되지 않겠죠!


블로그에는 빠르게 해결한 것처럼 기록했지만,
실제로는 리렌더링 원인을 찾기 어려워 꽤 어려운 시간을 보냈습니다! ㅋㅋ
useCallback에 대한 이해 부족이 그 원인이었던 것 같아요 🧐

하지만 이번 경험을 통해 하나의 개념을 잘 이해하게 되어서, 고민한 시간이 아깝지 않다고 생각해요!

➕LEVEL UP(+1)

profile
적응중

0개의 댓글

관련 채용 정보