위클리페이퍼

React: useMemo, useCallback, 생명 주기, key 설정 이유?

불닭냠냠 2024. 9. 1. 18:31

안녕하세요! 이번 포스트에서는 리액트에서 자주 사용되는 useMemo, useCallback Hook과 리액트의 생명주기(lifecycle), 그리고 배열을 렌더링할 때 꼭 알아야 하는 key 설정에 대해 알아볼게요. 리액트 예습 차원에서 검색하고 공부한 내용이라서 많이 부족합니다😊

 

목차

1. useMemo와 useCallback이란?

2. React에서의 생명주기

3. 배열을 렌더링할 때 key를 왜 설정해야하나?


useMemo와 useCallback이란?

리액트에서 useMemo와 useCallback은 성능 최적화를 위한 Hook입니다. 두 Hook 모두 불필요한 연산이나 함수 재생성을 방지하기 위해 사용됩니다. 말만 봐도 효율적일 것 같은데, 각각의 기능이 무엇인지 알아봅시다.

 

  • useMemo: 어떤 계산이 복잡하거나 시간이 많이 걸릴 때, 그 결과를 기억해뒀다가 나중에 똑같이 필요할 때 다시 계산하지 않고 기억해둔 값을 반환해줍니다. 예를 들어, 수천 개의 리스트를 정렬하는 작업이 자주 일어난다면, 그 결과를 useMemo로 기억해둔 뒤 다시 계산하지 않고 메모이제이션된 값을 사용해 효율적으로 처리할 수 있습니다.
  • useCallback: 함수가 자꾸 새로 생성되어 불필요한 리렌더링(rerendering)을 하는 경우에 막아주는 역할을 해요. useCallback은 함수를 기억해두고, 종속성 배열이 변경되지 않으면 같은 함수 참조를 계속 반환합니다. 이를 통해 자식 컴포넌트의 불필요한 리렌더링을 줄일 수 있습니다! 이 Hook은 첫 번째 인자로 고정시킬 함수를 받고, 두 번째 인자로는 디펜던시 리스트(dependencies array)를 받습니다.
  • 첫 번째 인자: 고정하고자 하는 함수입니다.
  • 두 번째 인자: 함수가 의존하는 값들의 배열입니다. 이 배열 내의 값들이 변경되지 않는다면, useCallback은 기존 함수를 그대로 재사용하게 됩니다.

언제 사용하면 좋을까?

  • useMemo: 데이터 연산이 복잡해서 매번 렌더링할 때 계산하는 것이 비효율적인 경우에 사용하면 좋습니다. 예를 들어, 수천 개의 리스트를 필터링하거나 정렬할 때 유용합니다.
const expensiveCalculation = useMemo(() => {
    return someComplexFunction(data);
}, [data]);
  • useCallback: 자식 컴포넌트에 콜백 함수를 props로 전달할 때 사용하면 좋습니다. 이때 함수가 불필요하게 재생성되지 않도록 해, 자식 컴포넌트의 불필요한 리렌더링을 방지할 수 있습니다.
const handleClick = useCallback(() => {
    doSomething();
}, [dependency]);

남용의 문제점

불필요한 연산이나 함수 재생성을 방지!

이 말을 들으면 효율적일 것 같지만, 남용하면 오히려 코드가 복잡해지고, 성능상 이점을 얻기 어려울 수 있습니다. 특히, useMemo와 useCallback이 관리하는 메모리도 비용이기 때문에, 지나치게 사용하면 메모리 누수처럼 자원을 비효율적으로 사용할 위험이 있습니다.

  • 오히려 느려질 수 있음: 불필요한 메모이제이션은 오히려 성능을 떨어뜨릴 수 있습니다. 리액트는 자체적으로 최적화가 잘 되어 있기 때문에, useMemo나 useCallback이 꼭 필요하지 않은 경우에는 사용하지 않는 것이 더 나을 수 있습니다.
  • 코드 가독성 저하: 메모이제이션 로직이 많아지면, 코드가 복잡해지고 가독성이 떨어질 수 있어요. 유지보수할 때도 어려움을 겪을 수 있습니다.

진짜 성능 문제가 발생할 때만 사용하는 것이 좋답니다!

 


React에서의 생명주기

생명 주기는 React 뿐만 아니라 많은 소프트웨어에서 쓰이고 있는 단어입니다. 다른 곳에서 많이 쓰인다는 것은 중요하다는 뜻이겠죠..?

 

그래서 리액트 또한! 태어나고, 업데이트되고, 사라지는 과정을 생명주기(lifecycle)라고 부릅니다. 이걸 잘 이해하면 컴포넌트를 더 잘 제어할 수 있어요. 생명주기는 크게 세 가지 단계로 나뉩니다:

생명주기 단계

리액트 컴포넌트의 생명주기는 크게 세 가지 단계로 나눌 수 있어요:

  1. 마운트(Mount): 컴포넌트가 처음 DOM에 등장할 때입니다. 여기서 constructor()로 초기 상태를 설정하거나, componentDidMount()로 API 호출 같은 초기 작업을 할 수 있어요.
  2. 업데이트(Update): 컴포넌트의 props나 state가 바뀔 때마다 일어나는 단계입니다. 이때 shouldComponentUpdate()로 컴포넌트가 업데이트될지 결정하거나, componentDidUpdate()에서 업데이트 후 필요한 작업을 수행할 수 있죠.
  3. 언마운트(Unmount): 컴포넌트가 DOM에서 사라질 때입니다. 이때 componentWillUnmount()로 타이머 해제나 구독 취소 같은 정리 작업을 해줄 수 있어요.

최근에는? 

최근에는 클래스형 컴포넌트보다는 함수형 컴포넌트를 많이 쓰면서, 생명주기 관리를 useEffect Hook으로 대체하는 경우가 많아요. useEffect는 마운트, 업데이트, 언마운트 시 실행할 로직을 간편하게 정의할 수 있어 정말 유용합니다!

 

(함수형, 클래스형 컴포넌트를 알고싶다면 이전 글을 참고해주세요!)

 

 


배열을 렌더링할 때 key를 왜 설정해야하나?

 

리액트에서 배열을 렌더링할 때는 key를 설정하는 것이 중요해요. key는 리액트가 각 항목을 고유하게 식별할 수 있도록 도와주는데, 이를 통해 효율적인 업데이트가 가능합니다. key는 리액트가 각 항목을 고유하게 식별할 수 있도록 도와주기 때문에, 리액트가 어떤 항목이 변경되었는지, 추가되었는지, 삭제되었는지를 효율적으로 추적할 수 있어요. key가 없거나 잘못 설정되면, 리액트는 어떤 요소가 변경되었는지 정확히 알 수 없어서 불필요한 리렌더링이 발생하거나, 예상치 못한 UI 오류가 생길 수 있어요.

 

key 설정 시 주의할 점

  • 고유한 값 사용: 배열 항목의 고유한 식별자를 key로 사용해야 합니다. 예를 들어 데이터베이스의 ID 같은 것이 적합해요.
  • 인덱스 사용 자제: 배열의 인덱스를 key로 사용하는 것은 피해야 해요. 항목의 순서가 바뀌면 리액트가 잘못된 항목을 업데이트할 위험이 있기 때문이죠.

잘못된 key 사용이 가져오는 문제

  • 성능 저하: 리액트가 잘못된 key로 인해 모든 항목을 새로 렌더링 렌더링하게 되면, 성능이 크게 저하될 수 있습니다.
  • UI 오류: 같은 key를 가진 항목이 있으면, 리액트는 key를 제대로 구분하지 못해 UI가 꼬일 수 있습니다.

 

 

리액트의 useMemo, useCallback Hook, 컴포넌트 생명주기, 그리고 배열 렌더링 시 중요한 key 설정에 대해 정리해봤습니다. 예습용으로 미리 글을 작성했지만, 이번 주에 내용을 다 배워서 다시 읽어보니 복습도 되면서 조금 더 깊이 이해할 수 있었던 것 같아요!  리액트는 아직도 배울 게 많은 것 같은데 더 열심히 익혀나가겠습니다.