[웹서비스 개발팀] 또 리액트 훅

Anna Park·2024년 7월 11일

webservice

목록 보기
16/33

오늘 배울것들

이번주 과제 예고

import React, {useState, useRef} from "react";

const UseRefApp2 = () => {
    const [renderer,setRenderer] = useState(0);
    const countRef = useRef(0);
    let countVar = 0;

    const doRendering = () => {
        setRenderer(renderer + 1);
        console.log("rendering")
    }
    const increaseRef = () => {
        countRef.current = countRef.current + 1;
        console.log("ref increased", countRef.current);
    }
    const increaseVar = () => {
        countVar = countVar + 1;
        console.log("var increased", countVar);
    }
    const printResult = () => {
        console.log(`ref: ${countRef.current}, var: ${countVar}`);
    }
    return(
        <div>
            <p>Ref: {countRef.current}</p>
            <p>Var: {countVar}</p>
            
            <button onClick={doRendering}>랜더</button>
            <button onClick={increaseRef}>Ref 올려</button>
            <button onClick={increaseVar}>Var올려</button>
            <button onClick={printResult}>Ref Var 올려</button>
            
        </div>
    )
}

export default UseRefApp2;

컴포넌트가 리렌더링될 때마다 초기값으로 재설정됨

Hook(useState, useEffect, useRef, useMemo 등)

Hook은 왜 쓰나요?

리액트의 컴포넌트들은 Lifecycle을 가지는데 이것을 제어할 수 있는 method들이 있습니다.
class형 컴포넌트에서만 사용가능한 method들이 있고 리액트 16.8버전부터 새로 도입된 기능으로 함수형 컴포넌트에서도 상태 관리를 할 수 있는 Hooks이 있습니다.

1.useState (renderer와 같은 state 변수)

  • 사용자 입력 관리 (폼 필드, 체크박스 등)
  • UI 상태 관리 (모달 열림/닫힘, 탭 선택 등)
  • API로부터 받아온 데이터 저장
  • 페이지네이션 상태 관리

2. useRef (countRef와 같은 ref)

  • DOM 요소에 직접 접근 (예: 포커스 설정, 스크롤 위치 조정)
  • 이전 state 값 저장
  • 타이머 ID 저장 (setInterval, setTimeout)
  • 컴포넌트 리렌더링 없이 값을 유지해야 할 때

3. 일반 변수 (countVar와 같은 변수)

  • 렌더링 중 일시적인 계산에 사용
  • 함수 내부에서 지역 변수로 사용
  • 리렌더링 사이에 유지되지 않으며, 매 렌더링마다 새로 초기화되므로 유지될 필요가 없는 임시 데이터 저장하는데 사용

4. useEffect

  • 컴포넌트의 Side Effects를 관리하는데 사용
  • API 호출, 구독 설정/해제, DOM 조작 등에 사용

5. useMemo

  • 계산비용이 높은 값을 메모이제이션하여 성능을 최적화하는 데 쓰임
  • 복잡한 계산이나 데이터 처리
  • 대규모 배열이나 객체의 필터링, 정렬, 변환
  • 부모 컴포넌트로부터 받은 prop을 기반으로 하는 계산

UseMemo를 왜 쓰나요?

useMemo를 사용하는 주요 이유는 성능 최적화입니다.

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
  1. 불필요한 재계산 방지:

    • 복잡한 계산이나 비용이 많이 드는 연산의 결과를 메모이제이션(기억)합니다.
    • 의존성이 변경되지 않으면 이전에 계산된 값을 재사용합니다.
  2. 렌더링 성능 향상:

    • 컴포넌트가 리렌더링될 때마다 모든 코드가 재실행되는 것을 방지합니다.
    • 특히 대규모 리스트나 복잡한 데이터 처리에서 유용합니다.
  3. 참조 동등성 유지:

    • 객체나 배열의 참조 동등성을 유지하여 불필요한 리렌더링을 방지합니다.
    • 이는 특히 prop으로 전달되는 값들에 중요합니다.
  4. 조건부 계산:

    • 특정 조건이 충족될 때만 계산을 수행할 수 있습니다.
  5. 컴포넌트 최적화:

    • React.memo와 함께 사용하여 컴포넌트의 불필요한 리렌더링을 방지할 수 있습니다.
  6. 의존성 배열을 통한 제어:

    • 언제 값을 재계산할지 명확하게 제어할 수 있습니다.

예를 들어, 제공된 코드에서:

const users = useMemo<D.IUser[]>(() => 
  D.makeArray(100).map(D.makeRandomUser)
, []);

이 부분은 100명의 랜덤 사용자를 생성하는 작업을 한 번만 수행하고, 이후 리렌더링에서는 이 결과를 재사용합니다. 이는 불필요한 계산을 방지하고 성능을 향상시킵니다.

그러나 useMemo를 과도하게 사용하면 오히려 메모리 사용량이 증가하고 초기 렌더링 시간이 늘어날 수 있으므로, 실제로 성능 향상이 필요한 경우에만 사용하는 것이 좋습니다.

Claude3.5:

실제 웹사이트 프론트엔드에서 useMemo의 사용 사례를 몇 가지 예시로 설명해드리겠습니다:

  1. 복잡한 데이터 필터링 및 정렬:

    const filteredAndSortedItems = useMemo(() => {
      return items
        .filter(item => item.category === selectedCategory)
        .sort((a, b) => b.price - a.price);
    }, [items, selectedCategory]);```
    
    대규모 상품 목록이 있는 e커머스 사이트에서 카테고리 필터링과 가격순 정렬을 수행할 때 사용할 수 있습니다.
    
  2. 차트 데이터 계산:

    const chartData = useMemo(() => {
      return salesData.map(sale => ({
        date: new Date(sale.date).toLocaleDateString(),
        revenue: sale.quantity * sale.price
      }));
    }, [salesData]);```
    
    대시보드나 분석 페이지에서 차트 데이터를 계산할 때 유용합니다.
    
  3. 폼 유효성 검사:

    const isFormValid = useMemo(() => {
      return email.includes('@') && password.length >= 8 && name.trim() !== '';
    }, [email, password, name]);

    회원가입 페이지 같은 곳에서 폼의 유효성을 검사할 때 사용할 수 있습니다.

  4. 검색 기능 구현:

    const searchResults = useMemo(() => {
      return products.filter(product => 
        product.name.toLowerCase().includes(searchTerm.toLowerCase())
      );
    }, [products, searchTerm]);

    제품 검색 기능을 구현할 때 사용할 수 있습니다.

  5. 페이지네이션 로직:

    const paginatedItems = useMemo(() => {
      const startIndex = (currentPage - 1) * itemsPerPage;
      return items.slice(startIndex, startIndex + itemsPerPage);
    }, [items, currentPage, itemsPerPage]);

    긴 목록을 페이지별로 나눠 표시할 때 유용합니다.

  6. 복잡한 스타일 계산:

    const dynamicStyles = useMemo(() => ({
      backgroundColor: isDarkMode ? '#333' : '#fff',
      color: isDarkMode ? '#fff' : '#333',
      fontSize: `${baseFontSize * fontScale}px`
    }), [isDarkMode, baseFontSize, fontScale]);

    테마나 사용자 설정에 따라 동적으로 스타일을 계산할 때 사용할 수 있습니다.

  7. API 응답 데이터 가공:

    const processedData = useMemo(() => {
      return apiResponse.map(item => ({
        ...item,
        fullName: `${item.firstName} ${item.lastName}`,
        age: new Date().getFullYear() - new Date(item.birthDate).getFullYear()
      }));
    }, [apiResponse]);

    API에서 받아온 데이터를 가공하여 화면에 표시할 때 유용합니다.

이러한 예시들은 계산 비용이 크거나 자주 사용되는 값들에 대해 useMemo를 적용하는 방법을 보여줍니다. 이를 통해 불필요한 재계산을 피하고 애플리케이션의 성능을 향상시킬 수 있습니다.

너무 어렵네요..
그래서 수업시간에 한 내용외에 참고할만한 자료 찾아보기

https://velog.io/@velopert/react-hooks

profile
교육개발, 웹서비스개발, 수학강사

0개의 댓글