배포 : https://tooth-is-silver.github.io/front_6th_chapter4-2/
과제 체크포인트
과제 요구사항
-
배포 후 url 제출
-
API 호출 최적화(
Promise.all이해) -
SearchDialog 불필요한 연산 최적화
-
SearchDialog 불필요한 리렌더링 최적화
-
시간표 블록 드래그시 렌더링 최적화
-
시간표 블록 드롭시 렌더링 최적화
과제 셀프회고
기술적 성장
profiler와 components 탭을 적절하게 사용하면서 메모이제이션이 얼마나 잘 동작하는지, 최적화가 잘 적용되어 눈에 띄게 빨라지는 애플리케이션의 속도를 보고 최적화의 큰 재미를 느꼈습니다. 특히 리렌더링 최적화 과정에서 context로 모두 나누기도 해보고, 컴파운드 패턴도 적용해보고 다양한 접근 방식을 시도해보며 많은 것을 배웠습니다.
적용해본 방향
- 합성 컴포넌트 패턴: 각 컴포넌트를 독립적으로 메모이제이션하여 불필요한 리렌더링을 방지
- Provider 위치 최적화: DndProvider를 필요한 영역에만 적용하여 리렌더링 범위 최소화
- 컴포넌트로 격리: 메모이제이션이 필요한 부분만 별도 컴포넌트로 격리하여 최적화
새로이 경험한 부분
- React.memo의 한계: props가 많고 복잡한 컴포넌트에서는 메모이제이션 효과가 미미할 수 있음
- 드래그 앤 드롭과 성능: 상호작용이 많은 UI에서는 단순한 메모이제이션만으로는 해결되지 않는 복잡한 문제들이 많다..
- Context 전파 메커니즘: Provider의 위치와 구조가 전체 렌더링 성능에 미치는 영향에 대해 잘 몰라 많이 헤매게 되었음
- ref를 이용한 최적화: useRef를 이용하여 리렌더링 트리거를 최소화하는 방향으로 활용 가능
- 컴포넌트 분리 기준: 컴포넌트 wrapper를 포함한 덩어리째로 분리하는 것보다 잘게 나누는 것이 렌더링 최적화에 더 좋다
코드 품질
드래그 앤 드랍시 테이블 내부의 Box 컴포넌트의 리렌더링이 많아 props를 메모이제이션하거나 최적화 할 수 있는 방향이 없을까 고민했습니다. 기존 리렌더에서 일부를 격리시킨다고 컴포넌트로 억지로 나누는 것 보다, 빠르게 최적화를 할 수 있는 부분을 찾아 먼저 진행하는 게 나을 것 같다고 판단했습니다. 작은 작업인 인라인 함수 메모이제이션부터 전역작업으로 크게 분류되는 context까지 단계별로 진행하려고 노력했습니다.
최적화를 하려면 데브 툴로 계속 추적 관찰을 해야하는데 코드 복잡성이 증가하는 상황에서, 눈에 보이는 효과가 큰 부분에 집중했습니다. 특히 그저 같은 상태를 쓰는 비즈니스 로직들을 context provider로 구현하려고했으나, 결국 잘게 나뉘어져버린 context로 인해 추적 및 고민하는 시간이 증가된 것을 경험했습니다. 굳이 context provider를 사용하지 않아도 메모이제이션으로 충분한 최적화가 되는 로직이 있는 반면에 스케쥴 데이터 관련 업데이트 로직들은 dnd에 종송되어 매번 새로운 참조가 생성되고 있었습니다. 초반엔 모든 로직들을 provider로 나누고자 했지만 한 번의 실패로 어느 부분을 context로 분리해야할지 확실히 배운 시간이었습니다. (4시간 동안 해결하려고 노력하였으나.. 해결되지 않아 다시 원점으로 돌아간 귀한 경험)
학습 효과 분석
React DevTools Profiler를 활용한 실제 리렌더링 분석 과정이 재밋었습니다. 메모이제이션을 하나씩 하면서 눈에 띄게 줄어든 리렌더링 횟수와 지연 시간 보면서 이전에는 구현만 하고 끝이었지만 프로덕트의 성능까지 되짚어보는 보는 눈을 가질 수 있게 되었습니다.
지연 로딩에 대해서도 확실하게 알게 된 시간이었습니다. 초반에 popover를 지연 로딩을 하기 위해 isPopoverOpen 상태를 사용해서 지연 로딩을 하는데 이 부분도 리렌더 이슈가 있을 경우 isPopoverOpen && <컴포넌트 /> 형식으로 최적화가 가능하다는 것을 정확히 알게 되었습니다. 대신 이번에는 isLazy로 사용하면 자연스러운 trasition효과가 적용되어 isOpen에서 해당 효과까지 적용하는 것보다 활용 방식을 이해하고자하는 것에 의의를 두는 것까지만 진행했습니다. 두 가지 모두 경험해본 다음에 isOpen은 두고 적용이 쉬운 isLazy로 변경하였습니다.
주변 팀원들한테 이것저것 물어보다보니 zustand나 jotai같은 상태 관리 라이브러리를 통해서도 최적화하신 분들이 있습니다. context provider만 생각했지만 해당 라이브러리를 통해서 얻을 수 있는 다른 이점이 있다면 어떤 부분일지 공부가 필요합니다.
아래의 내용은 AI한테 또 다른 성능 최적화 기업이 뭐가 있는지 물어본건데, Web Worker와 새로운 훅 사용도 적용해보고 싶습니다.
- Web Worker를 활용한 무거운 연산을 메인 스레드에서 분리하는 방법
- React 18의 useDefferedValue, useTransition을 활용하여 선능 최적화
과제 피드백
이번 경험을 통해 실무에서 성능 최적화 시 고려해야 할 우선순위를 스스로 직접 판단해볼 수 있었습니다:
- 먼저 React DevTools로 실제 성능 병목 지점을 정확히 파악
- 눈에 띄는 효과가 큰 부분부터 우선적으로 최적화 적용
- 코드 복잡성과 성능 향상 간의 균형점 찾기
- 과도한 최적화보다는 핵심적인 문제 해결에 집중
특히 대규모 프로젝트에서는 모든 부분을 최적화하기보다 사용자 경험에 직접적으로 영향을 주는 부분, 즉 과제로 따지면 dnd나 스케쥴 데이터 부분일 것 같습니다. 이러한 부분을 우선 순위를 주어 집중적으로 개선하는 것이 좋을 것 같습니다.
전반적으로 실무에서 마주할 수 있는 현실적인 성능 문제들을 직접 해결해볼 수 있어 재밋었습니다. 특히 완벽한 답이 없는 상황에서 최적의 해결책을 찾아가는 과정 자체가 큰 학습이 되었습니다. 물론 고민하는 시간은 엄청 오래걸렸지만요...
리뷰 받고 싶은 내용
- 최적화하고 싶었지만 하지 못했던 부분 질문
<Box
position="absolute"
left={`${120 + CellSize.WIDTH * leftIndex + 1}px`}
top={`${40 + (topIndex * CellSize.HEIGHT + 1)}px`}
width={CellSize.WIDTH - 1 + "px"}
height={CellSize.HEIGHT * size - 1 + "px"}
bg={bg}
p={1}
boxSizing="border-box"
cursor="pointer"
ref={setNodeRef}
transform={CSS.Translate.toString(transform)}
{...listeners}
{...attributes}
>
<Text fontSize="sm" fontWeight="bold">
{lecture.title}
</Text>
<Text fontSize="xs">{room}</Text>
</Box>
해당 코드는 제가 text부분만 따로 떼내에서 최적화를 진행했는데 이와 같은 경우에 Box는 최적화를 하지 않는 편이 나은지 그래도 최적화를 한다면 어떻게 최적화를 해주어야하는지 궁금합니다.
- 상처만 남은 div 최적화
과제 피드백
안녕하세요 가은님!! 마지막과제도 너무 잘 진행해주셨네요 ㅎㅎ 그동안 고생하셨어요!!! 포기하지 않고 끝까지 잘 수료해주셔서 감사합니다!
해당 코드는 제가 text부분만 따로 떼내에서 최적화를 진행했는데 이와 같은 경우에 Box는 최적화를 하지 않는 편이 나은지 그래도 최적화를 한다면 어떻게 최적화를 해주어야하는지 궁금합니다.
Box도 Text도 굳이 최적화할 필요는 없다고 생각해요 ㅎㅎ 아마 div와 p태그로 치환하면 더 좋지 않을까 싶기도 합니다 ㅋㅋ 여튼 이 컴포넌트들 때문에 버벅임이 생기진 않아서, 무시해도 무방해보여요!
현재 배포된 내용을 보면 dnd가 작동시 텍스트나 내부 gridItem은 리렌더되지 않으나 이동시키는 스케쥴 박스 div와 해당 시간표 내부의 다른 스케쥴 박스 div그리고 해당 시간표의 박스 div가 리렌더링 되는게 보입니다. div가 리렌더링 되는 부분을 찾아서 해결하고자해도 해결이 안되던데 chakra ui를 사용해서 생성된 이슈일까요? 아니면 다른 부분에서 오버 엔지니어링으로 provider를 최적화 할 수 있는 방법이 있었을지 궁금합니다. 이 부분 해결하고 최적화 깔끔하게 완료하고 싶었는데 시간을 가장 많이 들였는데도 불구하고 해결하지 못해서 궁금합니다.
이 부분은 charka-ui 때문이라기보단, dnd-kit을 통해 drag를 할 때 마다 계속 생성되는 함수 혹은 값 때문인 것 같아요 ㅎㅎ 다만 제가 가은님의 질문을 제대로 이해한건가..? 라는 생각이 먼저 드네요.. ㅠㅠ 언젠간 같이 확인해볼 수 있으면 좋겠어요 ㅋㅋㅋ
그동안 고생하셨습니다!!!