[최종 프로젝트 - 리팩토링] 성능최적화 올리기 (99점)

2024. 12. 17. 03:35·프론트엔드/Next.js

 

 

 

 

🤔 문제 발생

 

최종 프로젝트를 진행할때는 몰랐는데 지금 와서 리팩토링을 하다보니 사용성에 있어서 아쉬운 점이 보였다.

 

바로 탄소 배출량 계산 히스토리 부분이다.

여기에는 여러 작고 큰 문제들이 보이기 시작했다.

 

 

 

 

일단 몇가지 큰 오류가 있는데

 

1. 로딩화면

맨 처음 나오는 로딩화면은 훨씬 길다(캡쳐 시 크롭). 

 

2. 로딩 후 화면 이동

로딩 후 화면 이동 시 데이터가 제대로 들어가지 않고 순차적으로 들어오는 것을 볼 수 있다.

나는 분명 `Promise.all`을 이용해서 모든 데이터들을 묶어줬는데 왜 그런걸까..

 

3. 화면 재로딩

크롬에서 탭을 통해 다른 곳으로 이동했다 오면 다시 로딩이 된다. 그럼 위의 로직을 다시 타게 된다.

 

 

 

💻 기존 코드

const ResultPageMain = () => {
  const [userAvgData, setUserAvgData] = useState<number>(0);
  const [myAllData, setMyAllData] = useState<MonthlyData[] | null>(null);
  const [myAllAvgData, setMyAllAvgData] = useState<number>(0);
  const [userTopData, setUserTopData] = useState<TopData | null>(null);
  const [userAllData, setUserAllData] = useState<MonthlyData[] | null>(null);
  const [userInfo, setUserInfo] = useState<UserInfo | null>(null);
  const [isLoading, setIsLoading] = useState(true);

  const { user } = userStore();

  // 유저 이미지 가지고 오기
  const levelInfo = calculateLevelInfo(userInfo?.user_point ?? 0);

  useEffect(() => {
    const getUserFetch = async () => {
      const res = await getUserInfo(user.id);
      setUserInfo(res);
    };

    const fetchData = async () => {
      try {
        await Promise.all([
          loadUsersAvgData(setUserAvgData), // 유저 토탈 데이터
          loadMyData({
            setMyAllData,
            setMyAllAvgData,
            selectedYear: null
          }),
          loadTopUsersData(setUserTopData), // 유저 최고 데이터
          loadRecentFiveMonthsEmissions(currentYear, currentMonth, 2).then(
            (data) => {
              setUserAllData(data);
            }
          ),
          getUserFetch()
        ]);

        // 모든 데이터가 성공적으로 패칭되면 로딩 상태를 false로 변경
        setIsLoading(false);
      } catch (error) {
        console.error("데이터를 불러오는 중 오류가 발생했습니다:", error);
        setIsLoading(false);
      }
    };

    fetchData();
  }, [user]);

  if (isLoading) {
    return (
      <Loading
        message="탄소 배출량 히스토리 로딩 중"
        subMessage="잠시만 기다려 주세요~!"
      />
    );
  }

  console.log(myAllData);
...

 

 

 


 

 

💡 문제 해결

const ResultPageMain = () => {
  // 유저정보 가지고 오기
  const { user } = userStore();

  const { userInfo, isLoading: isUserInfoLoading } = useUserFetch(
    user?.id ?? null
  );

  // 유저 이미지 가지고 오기
  const levelInfo = calculateLevelInfo(userInfo?.user_point ?? 0);

  // 내 데이터값 가지고 오기
  const { data: carbonData, isLoading: isMyDataLoading } = useCarbonRecords({
    selectedYear: null
  });

  const { records: myAllData, avgEmission: myAllAvgData } = carbonData ?? {
    records: [],
    avgEmission: 0
  };

  // 유저 평균 데이터값 가지고 오기
  const {
    userAvgData,
    isLoading: isAvgDataLoading,
    error: userAvgDataError
  } = useUsersAvgData();

  // 제일 높은 데이터값 가지고 오기
  const {
    topUser,
    isLoading: isTopUserLoading,
    error: topUserError
  } = useTopUsersData();

  // 유저평균 최신달 2개 가지고 오기
  const {
    data: emissionsData,
    isLoading: isEmissionsLoading,
    error: emissionsError
  } = useRecentFiveMonthsEmissions(currentYear, currentMonth, 2);

  if (
    isMyDataLoading ||
    isAvgDataLoading ||
    isTopUserLoading ||
    isUserInfoLoading ||
    isEmissionsLoading
  ) {
    return (
      <Loading
        message="탄소 배출량 히스토리 로딩 중"
        subMessage="잠시만 기다려 주세요~!"
      />
    );
  }

  if (userAvgDataError || topUserError || emissionsError) {
    return <div>에러가 발생했습니다.</div>;
  }
  ...

 

 

기존 `useState` 사용했던 것을 `Hooks`나 `Tanstack Query` 사용으로 변경했다.

(이 페이지에서는 데이터가 갑자기 변경되는 내용이 없기때문에 딱히 `Tanstack Query`를 사용하지 않아도 괜찮았지만, 공부 차원에서 몇개만 변경을 했었다.)

 

처음 이 페이지에서 데이터를 페칭할때 `Promise.all`을 사용하면 데이터를 한번에 병렬로 가지고 오겠지? 라는 생각으로 작성을 했었는데 막상 구현을 해보니 그러지 않았다. 아마도 useEffect 내무에서 작성이 됨과 동시에 각자가 가지고 있는 로직 내부에서 또한 페칭 시 useEffect가 발행해서 여러번 출력이 되었던게 아닌가 싶다.

 

하지만 위와 같이 `hooks`나 `Tanstack Query`를 사용하니까 이런 중복적으로 데이터를 가지고 오거나 충돌이 나지 않아 데이터가 빨리 불러와 지게 된다.

실질적으로 약 8배 이상의 단축 시같을 보여줬다.

 

이렇게 해결하니 로딩화면 이후에 화면 이동이 되었을 때 즉각 적으로 데이터가 보였다.

 

마지막으로 `useEffect`에서 의존성배열에 `user`라는 값을 넣어 크롬에서 탭 이동 시 데이터가 다시 캐싱되는 부분이 있었는데 자연스럽게 해결이 되었다.

 

 

 


 

 

 

병렬 처리와 캐싱으로 더 빠르게 데이터를 로딩 할 수 있게 변경을 해봤는데 확실히 엄청나게 많은 성능 개선을 이룬 것 같다.

 

 

이번 영상은 로딩을 처음부터 다 보여줬는데 정말 빠르게 처리가 되는 것을 볼 수 있다. (배속 아님 ㅎㅎ)

 

 

 


 

 

 

그리고 마지막으로 라이트하우스를 통해 성능 개선한 점수를 보여주고 해당 글을 마무리하려고 한다.

 

왼쪽: 리팩토링 전

오른쪽 : 리팩토링 후

 

리팩토링 전에도 93점이면 높은 점수이긴 하지만 아마도 첫 데이터를 가지고 올 때 한번의 기준으로 정해진 듯 하다.

(여러번의 useEffect로 데이터를 관리하던 로직에 대해서는 정확하게 점수를 주지 못하는 것 같다.)

 

리팩토링 이후 99점이라는 매우 높은 점수로 올라갔다는 점과 여러 이슈들을 한번에 잡을 수 있었기 때문에 아주 좋은 리팩토링이 되지 않았나 싶다.

 

 

 

'프론트엔드 > Next.js' 카테고리의 다른 글

[최종 프로젝트 - 리팩토링] 컴포넌트 동적 활용  (0) 2024.12.04
[최종 프로젝트 - 리팩토링] props 구조분해할당  (0) 2024.12.02
[최종 프로젝트] 문자열 값에 대한 type 정의  (0) 2024.11.09
[최종 프로젝트] 비슷한 두개의 컴포넌트를 효율적으로 사용하기  (0) 2024.11.08
[최종 프로젝트] React 함수, 매개변수: 가독성을 높이는 작은 습관  (0) 2024.11.02
'프론트엔드/Next.js' 카테고리의 다른 글
  • [최종 프로젝트 - 리팩토링] 컴포넌트 동적 활용
  • [최종 프로젝트 - 리팩토링] props 구조분해할당
  • [최종 프로젝트] 문자열 값에 대한 type 정의
  • [최종 프로젝트] 비슷한 두개의 컴포넌트를 효율적으로 사용하기
장쫑이이
장쫑이이
  • 장쫑이이
    JongHoy
    장쫑이이
  • 전체
    오늘
    어제
    • 코딩공부하기 (41)
      • 프론트엔드 (41)
        • Next.js (12)
        • React (16)
        • TypeScript (2)
        • 상태관리 (1)
        • HTML&CSS (1)
        • 기타 (9)
      • 과거 공부 내용 (0)
        • JSCODE 그룹스터디 (0)
        • JS 객체 지향 프로그래밍 (0)
        • 리엑트 (0)
        • 제로초 - 웹 게임을 만들며 배우는 리엑트 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    slice
    styled-components
    setAttribute
    필터링
    팀프로젝트
    map
    github
    토글
    LastIndexOf
    Filter
    useState
    While문
    git
    reduce
    행렬의 덧셈
    isNan
    유클리드 호제법
    이중 for문
    destructuring
    속성 추가
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.1
장쫑이이
[최종 프로젝트 - 리팩토링] 성능최적화 올리기 (99점)
상단으로

티스토리툴바