과제 체크포인트
과제 링크
https://yuyeol.github.io/front_6th_chapter4-2/
과제 요구사항
-
배포 후 url 제출
-
API 호출 최적화(
Promise.all이해) -
SearchDialog 불필요한 연산 최적화
-
SearchDialog 불필요한 리렌더링 최적화
-
시간표 블록 드래그시 렌더링 최적화
-
시간표 블록 드롭시 렌더링 최적화
과제 셀프회고
기술적 성장
리액트 메모를 적극적으로 사용해 볼 수 있었습니다. 이전까지만해도 참조값이 거의 없는 컴포넌트에만 제한적으로 사용하는 것이 좋지 않나? 라고 생각했었지만, 이번에 극단적으로 리렌더가 많이되는 컴포넌트들 사이에서 최대한의 최적화를 진행해보면서 렌더링 최적화의 이점을 분명히 체감할 수 있었습니다.
이전에 코치님께서 리액트 메모와 콜백을 한 곳에 쓰기 시작하면 연쇄적으로 쓰이게 된다고 하셨던 이유도 이제는 참조 동일성을 유지시킨다는 관점에서 이해할 수 있게 되었습니다. 연결되는 함수나 객체가 새로 생성되면 참조 동일성이 깨지고, 연쇄 전파되기 때문에, 결국은 연관된 참조값들은 모두 메모해주어야 하는 이유를 잘 알 수 있었습니다.
또한 React.memo의 두 번째 인자(비교 함수)는 업데이트 트리거를 명시적으로 통제할 수 있다는 점은 처음 알게 된 내용입니다. 처음에는 명시적으로 리렌더를 트리거되는 값을 지정할 수 있는 점은 꽤 좋을것 같은데? 라는 생각이 들었지만, 필요한 업데이트를 무시하게 되어 의도치 않은 동작을 만들 수 있다는 위험도 있겠구나 하는 생각이 들었습니다.
리뷰 받고 싶은 내용
리뷰보다는 최적화에 관련된 질문이 몇가지 있습니다.
-
리액트 메모의 두번째 인자(비교 함수 작성)를 사용하여 리액트 메모를 제어하는 케이스가 많이 있나요? 개인적으로는 뭔가 useEffect, useMemo, useCallback의 의존성 배열에 자기가 넣고싶은 인자만 넣고 입맛대로 리렌더를 트리거 하도록 작성하는 것과 비슷하게 혼동을 줄것 같다는 생각이 듭니다.
-
이어지는 내용일 수 있는데 useEffect, useMemo, useCallback같은 훅의 의존성 배열에는 eslint에서도 경고가 뜨듯이 개발자 본인이 리렌더를 트리거하고싶은 의존성만 넣는 것이 아니라 필요한 의존성은 모두 넣어주는 것이 권장되는 것 같습니다. (모던 리액트 딥다이브 책에서 참고한 내용입니다.) 저도 실제로 그렇게 사용하고 있고, 복잡한 문제를 만나지 못해서 그랬는지 크게 불편함은 없는것 같습니다만, 코치님은 불가피하게 의존성을 eslint에서 경고하는 상태로 사용하게 되는 케이스가 있으셨나요?
-
불가피하게 useEffect에서 router.push를 사용해야 하는 경우가 있는것 같습니다. 예를 들면, 리렌더 이후 쿼리파라미터를 업데이트 해줘야 하는 케이스가 있을것 같은데요, useEffect에서 router.push를 하면 스트릭트모드 때문에 두번 라우팅이 되는데 이것은 용인되는 중복 실행일까요? 물론 프로덕트환경에서는 한번만 실행이되어서 상관이 없겠지만, 스트릭트모드 존재 의미자체가 중복실행이 되어서 문제 될 법한 것들은 하지 말라는 것 같은데 router.push도 결국은 상태를 변경하는 것이다 라는 관점으로 볼때 useEffect에서 실행되는 것이 어색한 것 같아서요.
과제 피드백
유열님 고생하셨어요! 10주동안 큰 성장이 있으셨던 것 같고 마지막 과제 또한 잘 마무리해주신것 같아요. 성능 최적화 관점에서 시도해주셨던 많은 내용들도 잘 정리해주셔서 덕분에 저도 많은것들 정리하고 배울 수 있었네요 ㅎㅎ
질문 주셨던것 호다닥 답변 남겨보면
Q. 비교함수
리액트가 등장한 초기에는 정말 많이 사용했던것 같은데, 요즘에는 꽤 많이 사용하지 않는것 같네요. 꽤 드물지만 정말 최대한의 성능 최적화를 하고 싶은 경우를 위해 열려있는 사례이고 종종 사용했던것 같아요. 명시적으로 인터페이스를 통해 제어를 열어둔 것이기 때문에 사용해도 무방하지만 말씀해주신것처럼 기존 동작과 다른 동작을 열어두는 것이기 때문에 혼동을 줄 수 있어요. 그래서 명확하게 어떤 목적에서 사용하는지 정리해주는게 필요합니다.
의존성 배열
종종 있지만 거의 없는 케이스인것 같아요 아마 예전 componentDidMount 라이프사이클 함수처럼 최초 1회만 실행하고 싶은 케이스가 주로 여기에 들어가는 것 같은데, 이 경우 일반적으로는 useRef나 로직을 분리하는 형태로 구현이 가능해서 거의 없는 것 같아요. 위에서 언급한것처럼 하지만 언제 어떤 최적화가 필요할 지 모르기 때문에 명확한 동작을 이해하고 필요할 때 동작에 맞게 사용하면 되지 않을까 싶습니다.
useEffect
좋은 질문인데요! 이 경우 실제 사례를 봐야겠지만.. 제가 알기로 strictMode의 의도는 클린업 로직 없이 반복실행될 수 없는 부수효과를 개발자가 찾기 쉽게 해놓은 것으로 이해하고 있는데요. 이 경우 말씀해주신것처럼 구현하면 실제로 두번의 라우팅이 일어나게 되죠. 용인되냐?라고 하면 사실 용인된다라기보단.. 프로덕션에서 문제가 없으니까 넘어간다 라는 관점이 맞는것 같아요. 가능하면 router.push를 이펙트에서 처리하는게 아닌 핸들러에서 처리하게 해야한다거나 정말 넣어야 하고, 불필요한 중복로딩을 제어해야 한다면 방어코드같은것을 넣어 제어할 수 있을 것 같아요.
고생하셨고, 앞으로 개발인생 화이팅 하세요! 그럼 내일봬요~