샘플 코드 준비하기

front-examples/examples/react-profiling-common-example at main · hanghae-plus/front-examples

# pnpm 설치
$ npm install -g pnpm

# git clone
$ git clone <https://github.com/hanghae-plus/front-examples>
$ cd front-examples

# 패키지 설치
$ pnpm install

# 샘플코드 실행
$ pnpm -F react-profiling-common-example dev

image.png

화면 기록 2024-10-01 오후 4.43.05.mov

(1) React Dev tools

React Developer Tools - Chrome 웹 스토어

일단 리액트 개발자도구를 먼저 설치해야 합니다.

React 개발자 도구 활용하기 (Dev Tools)

대략적인 사용 방법은 위의 포스트를 참고해주세요.

(2) 최적화 되지 않은 예제 코드

front-examples/examples/react-profiling-common-example/src/common-sample/origin/App.tsx at main · hanghae-plus/front-examples

import { PropsWithChildren, useState } from 'react';

type NewsCategory = '정치' | '경제' | '사회' | '문화';

interface NewsItem {
  id: number;
  title: string;
  category: NewsCategory
  likes: number;
  content: string;
}

const NEWS_CATEGORIES = ['정치', '경제', '사회', '문화'] as const;

const generateNewsData = (count: number): NewsItem[] => {
  return Array.from({ length: count }, (_, index) => ({
    id: index + 1,
    title: `뉴스 제목 ${index + 1}`,
    category: [...NEWS_CATEGORIES].sort(() => Math.random() - 0.5)[0],
    likes: Math.floor(Math.random() * 100),
    content: `이것은 뉴스 ${index + 1}의 내용입니다. 실제 내용은 더 길 것입니다.`
  }));
};

const newsItems = generateNewsData(50);

const Header = ({ totalLikes }: { totalLikes: number }) => (
  <header className="bg-blue-600 text-white p-4">
    <h1 className="text-2xl font-bold">뉴스 피드</h1>
    <p className="text-sm">총 좋아요 수: {totalLikes}</p>
  </header>
);

	const SidebarItem = ({
	selected = false,
	onClick,
	children
}: PropsWithChildren<{
  selected?: boolean,
  onClick?: () => void
}>) => {
  const className = selected
    ? 'bg-blue-500 text-white hover:bg-blue-600'
    : 'bg-gray-200 text-gray-800 hover:bg-gray-300';

  return (
    <button
      onClick={onClick}
      className={`w-full mb-2 px-4 py-2 rounded ${className}`}
    >
      {children}
    </button>
  );
};

const Sidebar = ({
	items,
	value,
	change,
}: {
  items: (NewsCategory | null)[]
  value: NewsCategory | null,
  change: (category: NewsCategory | null) => void,
}) => (
  <aside className="w-64 bg-white p-4">
    {items
      .map(item => (
        <SidebarItem
          key={item}
          selected={value === item}
          onClick={() => change(item)}
        >
          {item ?? '전체'}
        </SidebarItem>
      ))
    }
  </aside>
);

const NewsCard = ({ item, onLike }: { item: NewsItem, onLike: (id: number) => void }) => (
  <div className="bg-white p-4 mb-4 rounded shadow">
    <h2 className="text-xl font-semibold mb-2">{item.title}</h2>
    <p className="text-gray-600 mb-2">{item.content}</p>
    <div className="flex justify-between items-center">
      <span className="text-sm text-gray-500">{item.category}</span>
      <button
        onClick={() => onLike(item.id)}
        className="px-3 py-1 bg-red-100 text-red-800 rounded hover:bg-red-200"
      >
        좋아요 ({item.likes})
      </button>
    </div>
  </div>
);

const NewsFeed = ({ news, onLike }: { news: NewsItem[], onLike: (id: number) => void }) => (
  <main className="flex-1 p-4">
    {news.map((item) => (
      <NewsCard key={item.id} item={item} onLike={onLike}/>
    ))}
  </main>
);

const App = () => {
  const [news, setNews] = useState(newsItems);
  const [category, setCategory] = useState<NewsCategory | null>(null);

  const filteredNews = category
    ? news.filter(item => item.category === category)
    : news;

  const totalLikes = news.reduce((sum, item) => sum + item.likes, 0);

  const changeCategory = (newCategory: NewsCategory | null) => {
    setCategory(newCategory);
  };

  const likeFeed = (id: number) => {
    setNews(news.map(item =>
      item.id === id ? { ...item, likes: item.likes + 1 } : item
    ));
  };

  return (
    <div className="min-h-screen bg-gray-100">
      <Header totalLikes={totalLikes}/>
      <div className="flex">
        <Sidebar
          items={[null, ...NEWS_CATEGORIES]}
          value={category}
          change={changeCategory}
        />
        <NewsFeed news={filteredNews} onLike={likeFeed}/>
      </div>
    </div>
  );
};

export default App;

(3) 성능 프로파일링

1) 렌더링이 되는 컴포넌트를 바로 확인하기

image.png