ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 더 나은 UX를 위해 개발자가 할수있는 방법은?
    REACT 2026. 1. 14. 16:06
    “Make waiting times tolerable.
    Provide feedback during delays.

    도널드 노먼의 UX 원칙 중 하나는 모든 행동에는 적절한 피드백이 뒤따라야 한다는 것이다.

     

    UX에서 중요한 것은 기다림을 없애는 것이 아니라, 기다림을 경험하게 만드는 방식이다.

     

    디자인을 공부할때 UX는 기획자와 디자이너만의 영역이라 생각했는데
    생각해보면 개발자도 충분히 기여할수있는 방법이 많다 (보고있나 개발자여)

     

    그 방법 중 하나가 바로 성능최적화이다

    그래서 개발자에 있어서 아주~~~~!!!! 중요한 역량이다

    리액트에서 성능최적화를 하는 방법도 아주아주 많지만 가장 많이 쓰는 방법 두가지를 디깅해보았다!

     

    코드스플리팅

    하나의 거대한 JS 번들을 여러 조각으로 나누고, 필요한 코드만 로드하는 전략을 말한다.

    React와 같은 SPA 구조 라이브러리/프레임워크에서는

    보통 모든 페이지와 기능을 하나의 번들로 묶어 초기 로딩 시점에 한 번에 내려받는다.
    이 방식은 구현은 단순하지만, 사용자가 아직 사용하지도 않을 코드까지 함께 다운하게 만든다.

     

    React.lazy 와 Suspense 함께쓰기

    import { lazy, Suspense } from 'react'
    
    const ChartPage = lazy(() => import('./ChartPage'))
    
    function App() {
      return (
        <Suspense fallback={<Loading />}>
          <ChartPage />
        </Suspense>
      )
    }

     

    ChartPage는 당장 필요한 코드가 아니기 때문에 lazy로 초기 번들에 포함되지 않게 만들었다. 

    그리고 렌더링 시점에 JS 파일을 네트워크로 가져오는 동안 Suspense로 fallback (대체 UI)로 상태를 전달한다

     

    여기서의 성능을 최적화는 lazy 뿐이지만 노먼 선생님이 말한대로 기다리는 동안 아무 피드백이 없으면 사용자는 답답해질것이다

    그래서 UX개선과 lazy의 비동기 처리를 위해 Suspense를 함께 쓰는 전략을 고르는것이다.

     

     

    만약 라우트를 쓴다면 라우트 단위로 스플리팅하기

    const Home = lazy(() => import('./pages/Home'))
    const Detail = lazy(() => import('./pages/Detail'))

     

    요렇게 lazy를 통해 페이지 단위로 import 시켜주면 초기로딩 비용도 줄이고
    사용자도 한번에 한페이지만 사용할수있으니 일석이조!

     

    메모제이션 쓰는 방법도 아주 좋다!

    지난번 메모제이션에 대해 포스팅한 것이 있기때문에 이번에는 간단히 설명해보자면

    리액트에선 state props가 바뀌면 컴포넌트가 리렌더링 되기때문에

    이과정에서 state가 바뀌지않았는데 비용이 큰 계산이 로직에 의해 매번 실행된다거나 과한 props drilling이 있다면

    React.memo, useMemo, useCallback 같은 메모제이션을 쓰는 것도 좋은 전략중 하나가 된다

     

    요것에 대한 설명은 지난날 포스팅을 참고하도록...

    2025.12.29 - [REACT] - Virtual DOM부터 Memoization까지

     

    Virtual DOM부터 Memoization까지

    기나긴 2주간의 방학이 끝나고 다시 수업이 재개되었다.. 그리고 오랜만의 디깅타임~!!!오늘은 가상돔과 메모이제이션으로 최적화할 수 있는 방법들을 두루 배웠다.역시나 면접 단골 질문으로

    lyla-bae.tistory.com

     

     

    메모제이션 실습 문제로 마무리!

    실전 같이 성능 최적화하기 try-first

    React.memo, useMemo, useCallback을 조합해서 최적화하는 고급 실습입니다.

    실무에서 자주 맞닥뜨리는 패턴이고, 특히 리스트 & 자식 컴포넌트가 있을 때 성능 최적화가 중요해요.

    현재 우리가 개발 중인 프로젝트에서 게시글 리스트를 렌더링하는 부모 컴포넌트가 있고

    각 게시글(자식 컴포넌트)은 props에 따라 무거운 연산을 하거나 버튼 클릭 이벤트를 가지고 있는 상태입니다.

     

    더보기

     

    // PostItem.tsx
    const PostItem = ({ title, content, onClick }) => {
      console.log(`📄 Rendering: ${title}`);
    
      // 무거운 연산 (예시)
      const wordCount = content
        .split(" ")
        .reduce((acc, word) => acc + word.length, 0);
    
      return (
        <div style={{ border: "1px solid gray", marginBottom: "10px" }}>
          <h4>{title}</h4>
          <p>{content}</p>
          <p>
            <b>Word Count:</b> {wordCount}
          </p>
          <button onClick={onClick}>Like</button>
        </div>
      );
    };
    
    // App.tsx
    import { useState } from "react";
    
    const DUMMY_POSTS = [
      { id: 1, title: "First Post", content: "This is the first post content." },
      {
        id: 2,
        title: "Second Post",
        content: "Another post with more words to count.",
      },
      { id: 3, title: "Third Post", content: "Short one." },
    ];
    
    export default function App() {
      const [likes, setLikes] = useState(0);
      const [dummy, setDummy] = useState(false);
    
      return (
        <div>
          <h2>Total Likes: {likes}</h2>
          <button onClick={() => setDummy(!dummy)}>Re-render Parent</button>
    
          {DUMMY_POSTS.map((post) => (
            <PostItem
              key={post.id}
              title={post.title}
              content={post.content}
              onClick={() => setLikes(likes + 1)}
            />
          ))}
        </div>
      );
    }

    위 코드를 실행시켜보면 모든 버튼을 누르면 모든 자식 컴포넌트들이 리렌더링됩니다.

    이 예제에서 뭐가 문제인지, 그 문제를 어떻게 해결할 수 있을 지 수정해보세요.

     

     

    결과

    풀이

    // PostItem.jsx
    import { memo, useMemo } from "react";
    
    export const PostItem = memo(({ title, content, onClick }) => {
      // props 비교해 메모제이션
      console.log(`📄 Rendering: ${title}`);
    
      // 무거운 연산이 반복되므로 useMemo로 content,title이 바뀔때만 다시계산
      const wordCount = useMemo(() => {
        return content.split(" ").reduce((acc, word) => acc + word.length, 0);
      }, [content, title]);
    
      return (
        <div style={{ border: "1px solid gray", marginBottom: "10px" }}>
          <h4>{title}</h4>
          <p>{content}</p>
          <p>
            <b>Word Count:</b> {wordCount}
          </p>
          <button onClick={onClick}>Like</button>
        </div>
      );
    });

     

    자식 컴포넌트부터 문제를 살펴보면 

    1. PostItem 컴포넌트의 props들부터 불필요하게 리렌더링 되고 있었다.

    따라서 React.memo로 props를 비교해 메모제이션 시켜주고

     

    2. wordCount 란 무거운 연산 또한 content와 title이 바뀔때만 다시 계산되도록

    useMemo로 메모제이션 시켜주었다.

     

     

    // App.tsx
    import { useCallback, useState } from "react";
    import { PostItem } from "./PostItem";
    const DUMMY_POSTS = [
      { id: 1, title: "First Post", content: "This is the first post content." },
      {
        id: 2,
        title: "Second Post",
        content: "Another post with more words to count.",
      },
      { id: 3, title: "Third Post", content: "Short one." },
    ];
    
    export default function App() {
      const [likes, setLikes] = useState(0);
      const [dummy, setDummy] = useState(false);
    
      //매번 실행하던 함수를 useCallback 빈배열(의존성배열)로 한번만 생성할수있게
      const handleLike = useCallback(() => {
        setLikes((prev) => prev + 1);
      }, []);
    
      return (
        <div>
          <h2>Total Likes: {likes}</h2>
          <button onClick={() => setDummy(!dummy)}>Re-render Parent</button>
    
          {DUMMY_POSTS.map((post) => (
            <PostItem
              key={post.id}
              title={post.title}
              content={post.content}
              onClick={handleLike}
            />
          ))}
        </div>
      );
    }

     

    3. 버튼의 이벤트 핸들러도 이전코드엔 App 컴포넌트가 실행될때마다 내용이 같아도 매번 생성되기 때문에

    요거또한 useCallback의 의존성배열을 활용해서 클릭할때 딱 한번만 생성될수 있게 만들어주면 끝!

     

Designed by Tistory.