반응형

 

React 애플리케이션을 개발할 때 성능 최적화는 중요한 핵심 요소입니다. 렌더링 성능을 제대로 최적화하지 않으면 불필요한 리렌더링으로 인해 앱의 전반적인 반응성이 크게 저하될 수 있습니다. 이번 글에서는 TypeScript 기반으로 useMemo, useCallback, React.memo를 활용한 성능 최적화 방법을 상세히 알아보겠습니다.

 

💂‍♀️ React 성능 최적화가 필요한 이유

React는 상태(state)나 props가 변경될 때마다 컴포넌트를 자동으로 다시 렌더링합니다. 그러나 모든 렌더링이 실제로 필요한 것은 아닙니다. 불필요한 렌더링이 과도하게 발생하면 애플리케이션의 성능이 현저하게 저하될 수 있습니다. 이러한 문제를 예방하고 개선하기 위해 메모이제이션(memoization) 기법을 효과적으로 활용할 수 있습니다. 

 

React에서 제공하는 최적화를 위한 hook 

1. useMemo → 값을 메모이제이션하여 불필요한 연산 방지
2. useCallback → 함수를 메모이제이션하여 불필요한 함수 재생성 방지
3. React.memo → 컴포넌트를 메모이제이션하여 불필요한 리렌더링 방지

 

하나씩 예제를 살펴보겠습니다. 

 

1. useMemo : State의 메모제이션

useMemo는 많이 소요되는 연산 결과를 저장하고, 의존성이 변경되지 않는다면 재계산하지 않도록 합니다.

즉, 불필요한 연산을 방지할 수 있습니다. 그러나 잘못 사용하게되면 오히려 오버헤드가 발생할 수도 있습니다.

import { useState, useMemo } from "react";

const ExpensiveComponent = () => {
  const [count, setCount] = useState<number>(0);
  const [text, setText] = useState<string>("");

  // 연산 비용이 큰 작업 (ex: 배열의 합 구하기)
  const expensiveValue = useMemo(() => {
    console.log("expensiveValue 계산 중...");
    return Array.from({ length: 10000 }, (_, i) => i).reduce((a, b) => a + b, 0);
  }, []); // 의존성 배열이 빈 배열이므로 한 번만 계산됨

  return (
    <div>
      <p>비싼 연산 결과: {expensiveValue}</p>
      <button onClick={() => setCount(count + 1)}>Count 증가: {count}</button>
      <input value={text} onChange={(e) => setText(e.target.value)} />
    </div>
  );
};

export default ExpensiveComponent;

2. useCallback: 함수의 메모제이션

useCallback은 함수의 재생성을 방지하여, 자식 컴포넌트에 전달할 때 불필요한 리렌더링을 막아줍니다.
자식 컴포넌트가 불필요하게 렌더링되지 않으나 확실히 의존성이 많아지면 코드가 점점 복잡해 질 수 있습니다. 

import { useState, useCallback } from "react";

const ChildComponent = ({ onClick }: { onClick: () => void }) => {
  console.log("ChildComponent 렌더링");
  return <button onClick={onClick}>클릭</button>;
};

const ParentComponent = () => {
  const [count, setCount] = useState<number>(0);

  // useCallback을 사용하여 함수 재생성을 방지
  const handleClick = useCallback(() => {
    console.log("버튼 클릭!");
  }, []);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
      <ChildComponent onClick={handleClick} />
    </div>
  );
};

export default ParentComponent;

3. React.memo : 컴포넌트의 메모제이션

React.memo는 props가 변경되지 않는 한 컴포넌트를 다시 렌더링하지 않도록 합니다.
불필요한 리렌더링을 방지할 수 있지만 useMemo와 useCallback과 함께 사용해야하는 경우가 생기면 코드가 점점 복잡해집니다. 

import React, { useState } from "react";

const MemoizedChild = React.memo(({ count }: { count: number }) => {
  console.log("MemoizedChild 렌더링");
  return <p>카운트 값: {count}</p>;
});

const ParentComponent = () => {
  const [count, setCount] = useState<number>(0);
  const [text, setText] = useState<string>("");

  return (
    <div>
      <MemoizedChild count={count} />
      <button onClick={() => setCount(count + 1)}>+1</button>
      <input value={text} onChange={(e) => setText(e.target.value)} />
    </div>
  );
};

export default ParentComponent;

 

 

최적화의 중요성은 잘 알고 있지만 어떠한 상확에 적절하게 적용하는건 생각보다 어렵습니다. 모든 곳에 사용하지 말고 필요한 곳에 적절히 사용하는것이 중요해 보입니다. 😏

반응형

'React' 카테고리의 다른 글

NextJS 에서 SEO 설정하기  (0) 2025.03.31
요즘 대세인 react 전역 상태관리 jotai 와 zustand  (0) 2025.03.28
커스텀 hook 만들기  (0) 2025.01.31
한글 초성 추출 하기  (7) 2023.10.10
React에서 clipboard에 copy&paste 하기  (0) 2023.10.09
반응형

SEO 를 설정하면 Google, Bing 과 같은 검색 엔진에 노출시켜 클롤링과 색인화 할 수 있습니다. 즉, 검색을 할때 잘 나타납니다. 주의할 점은 개발 중일때는 검색이 되면 안되니 주로 설정하지 않고 운영에만 설정해서 배포하도록 합니다. 또한 Facebook이나 Twitter, Kakaotalk 등 소셜 미디어에 링크를 공유할때도 어떤 페이지를 공유 했냐에 따라 썸네일 이미지나 제목등의 미리보기를 커스텀 할 수도 있습니다. 또한 검색 엔진에 의해 페이지의 성능과 접근성에 대한 적절한 판단을 받아 사용자 경험도 함께 향상되기 때문에 좋은 사이트가 됩니다. 😀

 

요즘은 다양한 라이브러리가 나와있기 때문에 손쉽게 설정이 가능합니다. 

다양한 방법이 있지만 크게 header에 직접 추가하거나 next-seo 라이브러리를 사용합니다. 

 

1. Header 에 직접 추가하기 

import Head from "next/head";

export default function Home() {
  return (
    <>
      <Head>
        <title>내 블로그 제목</title>
        <meta name="description" content="이 블로그는 SEO를 최적화한 Next.js 예제입니다." />
        <meta name="keywords" content="Next.js, SEO, React, 웹개발" />
        <meta name="author" content="작성자 이름" />

        {/* Open Graph (Facebook, 카카오톡 등) */}
        <meta property="og:title" content="내 블로그 제목" />
        <meta property="og:description" content="이 블로그는 SEO를 최적화한 Next.js 예제입니다." />
        <meta property="og:image" content="/og-image.png" />
        <meta property="og:url" content="https://myblog.com" />
        
        {/* Twitter Card (트위터 공유) */}
        <meta name="twitter:card" content="summary_large_image" />
        <meta name="twitter:title" content="내 블로그 제목" />
        <meta name="twitter:description" content="이 블로그는 SEO를 최적화한 Next.js 예제입니다." />
        <meta name="twitter:image" content="/twitter-image.png" />
      </Head>

      <h1>SEO 최적화된 블로그</h1>
      <p>이 페이지는 검색 엔진 최적화가 적용되었습니다.</p>
    </>
  );
}

 

📌 next/head 주의사항

  • title과 meta 태그는 페이지별로 맞춤 설정하는 것이 좋음.
  • 이미지(og:image, twitter:image 등)는 절대 경로로 설정해야 함.

2. next/seo 라이브러리 사용하기 

npm i next-seo

 

npm 으로 간단한게 설치가 가능합니다. 기전에 header 에 직접 추가하는 것보다 컴포넌트 단위로 수월하게 setting이 가능해서 실제 프로젝트에서 많이 사용합니다.

 

import { NextSeo } from "next-seo";

export default function Home() {
  return (
    <>
      <NextSeo
        title="내 블로그 제목"
        description="이 블로그는 SEO를 최적화한 Next.js 예제입니다."
        canonical="https://myblog.com"
        openGraph={{
          url: "https://myblog.com",
          title: "내 블로그 제목",
          description: "이 블로그는 SEO를 최적화한 Next.js 예제입니다.",
          images: [{ url: "https://myblog.com/og-image.png", width: 800, height: 600, alt: "Og Image Alt" }],
          site_name: "MyBlog",
        }}
        twitter={{
          handle: "@mytwitterhandle",
          site: "@site",
          cardType: "summary_large_image",
        }}
      />

      <h1>SEO 최적화된 블로그</h1>
      <p>이 페이지는 검색 엔진 최적화가 적용되었습니다.</p>
    </>
  );
}

 

하나의 컴포넌트에 다양한 옵션을 줄 수 있어서 meta  태그를 일일이 선언하지 않아서 편리합니다. defaultSeo 를 사용하면 모든 페이지에 공통 적용도 가능합니다. 

 

반응형
반응형

요즘 가장 대세처럼 많이 사용한다고 여기저기 사용한 코드를 인수인계 받거나 블로그 글도 많이 보았는데, 그래서 정확히 뭔지 궁금해짐. 둘 다 전역 상태관리 라이브러리 아닌가? 그래서 두 라이브러리가 어떻게 다를까? 

 

Jotai

jotai의 특징들 

- Atomic 한 상태관리 (Recoil 과 유사)

- atom() 을 사용한 개별적인 상태를 정의 할 수 있음

- 이렇게 개별화된 atom을 useAtom() 으로 사용 가능 

- 개별 atom단위로 구독

- atomWithAsync() 등등 으로 비동기 사용

- 상태가 독립적이거나 세분화된 경우에 사용하기 좋음

 

import { atom, useAtom } from 'jotai';

const countAtom = atom(0); // atom() 으로 상태 정의 

function Counter() {
  const [count, setCount] = useAtom(countAtom); // useAtom 으로 사용  

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button> // setCount() 를 통한 개별 업데이트 
    </div>
  );
}

export default Counter;

 

 

Zustand

- Store 기반한 상태관리 (Redux 와 유사)

- create() 를 사용한 상태 정의와 액션 정의

- useStore(), useZustand() 로 상태 업데이트 

- Store의 부분 상태를 구독 가능 

- set() 으로 비동기 로직 사용 가능

- 여러 상태를 한 번에 관리할 수 있음 

import { create } from 'zustand';

const useCounterStore = create((set) => ({ // store 정의 
  count: 0,
  increase: () => set((state) => ({ count: state.count + 1 })), // set() 으로 상태를 변경 
}));

function Counter() {
  const { count, increase } = useCounterStore(); // 상태 구독

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increase}>+1</button>
    </div>
  );
}

export default Counter;

 

최종 결론

둘 중 하나만 써도 무방하겠지만 각각 필요한 역활에 조합해서 쓰는게 좋아보인다. 

데이터 관리는 zustand로 store에 저장하고, 테마 변경등의 독립적으로 상태를 구성하는 거는 jotai로 관리하는걸 추천한다고 한다. 

 

import { atom, useAtom } from 'jotai';
import { create } from 'zustand';

// Zustand Store 정의
const useUserStore = create((set) => ({
  user: { name: '홍길동', age: 25 },
  setUser: (user) => set({ user }),
}));

// Jotai 상태 정의
const themeAtom = atom('light');

function Profile() {
  const { user, setUser } = useUserStore();
  const [theme, setTheme] = useAtom(themeAtom);

  return (
    <div style={{ background: theme === 'light' ? '#fff' : '#333', color: theme === 'light' ? '#000' : '#fff' }}>
      <h2>{user.name}</h2>
      <button onClick={() => setUser({ name: 'Bob', age: 30 })}>Change Name</button>
      <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>Toggle Theme</button>
    </div>
  );
}

export default Profile;

 

라떼는 redux 가 거의 유일무이였는데, 이제 다양한 관리 라이브러리도 나오는 걸 보니 계속 공부해야 겠음 

반응형

'React' 카테고리의 다른 글

React 성능 최적화: useMemo, useCallback, React.memo  (0) 2025.04.01
NextJS 에서 SEO 설정하기  (0) 2025.03.31
커스텀 hook 만들기  (0) 2025.01.31
한글 초성 추출 하기  (7) 2023.10.10
React에서 clipboard에 copy&paste 하기  (0) 2023.10.09

+ Recent posts