yhun940731 님의 상세페이지[7팀 최용훈] Chapter 4-2 코드 관점의 성능 최적화

과제 체크포인트

과제 요구사항

  • 배포 후 url 제출 https://yhun940731.github.io/front_6th_chapter4-2/

  • API 호출 최적화(Promise.all 이해)

  • SearchDialog 불필요한 연산 최적화

  • SearchDialog 불필요한 리렌더링 최적화

  • 시간표 블록 드래그시 렌더링 최적화

  • 시간표 블록 드롭시 렌더링 최적화

과제 셀프회고

기술적 성장

캐싱

캐시 적용을 위해 아래 캐시 클래스를 생성해서 export한다.

import { AxiosResponse } from "axios";
import { Lecture } from "../types";

class CacheStorage<T> {
  private storage: Map<string, T>;
  constructor() {
    this.storage = new Map();
  }

  get(key: string): T | undefined {
    return this.storage.get(key);
  }

  set(key: string, value: T): void {
    this.storage.set(key, value);
  }

  has(key: string): boolean {
    return this.storage.has(key);
  }

  clear(): void {
    this.storage.clear();
  }
}

export const cache = new CacheStorage<Promise<AxiosResponse<Lecture[], unknown>>>();
const fetchMajors = () => {
  if (cache.has(SCHEDULES_MAJORS)) {
    return cache.get(SCHEDULES_MAJORS);
  }

  const promise = axios.get<Lecture[]>(SCHEDULES_MAJORS);

  cache.set(SCHEDULES_MAJORS, promise);

  return promise;
};

const fetchLiberalArts = () => {
  if (cache.has(SCHEDULES_LIBERAL_ARTS)) {
    return cache.get(SCHEDULES_LIBERAL_ARTS);
  }

  const promise = axios.get<Lecture[]>(SCHEDULES_LIBERAL_ARTS);

  cache.set(SCHEDULES_LIBERAL_ARTS, promise);

  return promise;
};

// TODO: 이 코드를 개선해서 API 호출을 최소화 해보세요 + Promise.all이 현재 잘못 사용되고 있습니다. 같이 개선해주세요.
const fetchAllLectures = async () => {
  return await Promise.all([
    (console.log("API Call 1", performance.now()), fetchMajors()),
    (console.log("API Call 2", performance.now()), fetchLiberalArts()),
  ]);
};

이런식의 진행이 맞을지는 모르겠지만.. 캐싱처리를 시도했다..

Set 기반 검색

// Before: O(n) 배열 검색
grades.includes(lecture.grade);

// After: O(1) Set 검색
const gradesSet = useMemo(() => new Set(grades), [grades]);
gradesSet.has(lecture.grade);

// Before: O(n) 복잡도
const isSelected = selectedItems.includes(item);

// After: O(1) 복잡도
const selectedSet = useMemo(() => new Set(selectedItems), [selectedItems]);
const isSelected = selectedSet.has(item);

반복적인 코드에서 배열 순회를 최소한으로 하였다.

디바운싱

const useDebounce = (value: string, delay: number) => {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const timer = setTimeout(() => setDebouncedValue(value), delay);
    return () => clearTimeout(timer);
  }, [value, delay]);

  return debouncedValue;
};

검색어 타이핑 시 디바운스를 적용하여 리렌더를 줄였다.

학습 효과 분석

성능 최적화를 위해 뭔가 해보려고는 했는데, 결정적인 Dnd 리렌더, 다이얼로그 렌더링 최적화 등 부족함을 느끼고있다. 많은 시간을 할애하지는 못했지만, 나만의 최적화 노하우가 없는 기분...

앞으로 회사 프로덕션 코드도 자세하게 보면서 성능 최적화 방식에 대해 좀 더 노력해봐야할 것 같다.

리뷰 받고 싶은 내용

위에서 진행한 최적화 방식들이 정말 효과가 있을까 싶습니다. 성능을 해치던 가장 큰 원인은 Dnd와 스크롤링 같은데, 저런 자잘한 것도 영향을 끼치긴할까요... 브라우저 성능이 워낙 좋다보니 저렇게 사소한 것에는 조금 회의적인 입장입니다..

과제 피드백

수고했어요 용훈!! 이번 과제는 React 애플리케이션에서 실제 성능 병목 지점을 찾고 최적화하는 것이 목표였습니다.

CacheStorage 클래스로 캐싱 시스템을 직접 구현한 부분 잘했어요. Promise를 캐시하고 Map을 사용해서 O(1) 접근 성능도 확보했네요. 같은 맥락으로 Set 기반 검색 최적화도 진행을 했군요. 좋습니다. 캐싱과 접근방식을 개선하는 건 최적화를 하는 하기 위한 가장 기초가 되는 기본 개념이죠.

또한 디바운싱을 통해서 시간내에 비싼 동작을 해야한다면 skip해도 무방한 것들은 하지 않도록 하는 접근법도 최적화를 하는데 있어서 중요한 개념입니다. 우리가 최적화를 할때 memo를 하는 것도 이러한 캐시와 접근법과 skip을 하는 것들이 혼합된 개념이죠.

Q) 위에서 진행한 최적화 방식들이 정말 효과가 있을까 싶습니다. 성능을 해치던 가장 큰 원인은 Dnd와 스크롤링 같은데, 저런 자잘한 것도 영향을 끼치긴할까요... 브라우저 성능이 워낙 좋다보니 저렇게 사소한 것에는 조금 회의적인 입장입니다..

=> 성능 최적화를 할 때에는 최소한의 노력으로 극적인 개선이 있는 방법을 쓰는게 좋죠. 디바운스와 비슷한 접근법으로 드래그나 스크롤같이 연속적으로 발생하는 이벤트에 대해서도 적용을 해봤으면 좋았겠네요. 캐시를 만들었던처럼 memo를 고민해봤을수 도 있을거에요.

=> 자잘한 것도 영향을 미치긴 하죠. 1/10로 성능이 줄어든다고 했을때 0.1초에서 0.01초로 주는건 미비하다 느겨지겠지만 1s -> 0.1초로 줄면 엄청난 체감이니까요.

=> 용훈이가 생각한대로 미비한 개선보다는 기왕이면 접근법은 같아도 현재 가장 큰 성능상의 병목이 어딘가를 이용해서 적용을 하는게 좋겠죠. 그러기 위해서는 DevTools Profiler를 적극 활용봐야 합니다. 그래서 용훈이 한 최적화가 병목의 이유였다면 같은 방법이지만 효과가 있었겠지요.

=> 회사 코드에서 성능 이슈를 찾을 때는 lighthouse 점수나 Core Web Vitals부터 체크해보세요. LCP, FID, CLS 같은 실제 사용자 경험 지표에서 문제가 있는 부분을 우선적으로 최적화하면 더 의미있는 결과를 얻을 수 있어요.

성능 최적화는 어떻게 하는지 방법보다도 어디서 병목이 생기는지를 이해하고 찾는 게 더 큰 실력이라고 생각해요. 심심할때마다 어디가 병목이 될까 등을 한번씩 고민해보다보면 이러한 경험이 쌓여 나중에 복잡한 성능 문제를 만났을 때 어디서부터 접근할지 감이 생기는 거라 생각해요. :)

지난 10주간 너무 너무 수고 많았어요! 이번 경험과 추억들이 앞으로 개발을 하는데 있어서 나 스스로 충분히 잘하려고 노력할 수 있구나 해냈구나 하는 마음으로 새로운 것에 도전하고 성장하는데 있어 더 쉽게 도전할 수 있게 되는 계기가 되기를 바랍니다. 화이팅입니다