[6팀 김수현] Chapter 1-3. React, Beyond the Basics#35
[6팀 김수현] Chapter 1-3. React, Beyond the Basics#35suhyeon57 wants to merge 15 commits intohanghae-plus:mainfrom
Conversation
|
안녕하세요 수현님! useRef로 함수 참조를 고정하는 방식은 Lint 경고도 없고 잘 작동하긴 하지만, React의 선언형 흐름(상태 기반 UI 갱신)을 우회해 직접 제어하는 명령형 방식이라 유지보수나 협업 관점에서는 주의가 필요한 부분이라고 생각할 수 있을꺼 같아요! 저는 이 부분을 React의 선언형 흐름을 유지하는 방향으로 구현해보고자, useRef 대신 useMemo와 useCallback을 조합해 아래와 같이 구현해봤습니다! const { show, hide } = useMemo(() => createActions(dispatch), [dispatch]);
const hideAfter = useMemo(() => debounce(hide, DEFAULT_DELAY), [hide]);
const showWithHide: ShowToast = useCallback((...args) => {
show(...args);
hideAfter();
}, [show, hideAfter]);이렇게 하면 ref 없이도 함수 참조를 안정적으로 유지할 수 있고, 의존성 배열 관리도 명확하게 관리할 수 있어 React스럽고 유지보수에도 더 좋다고 생각했습니다! 제 말이 틀렸거나 다르게 생각하시면 언제든 피드백 부탁드립니다 :) |
|
오 희진님 !! 감사해요 이렇게 하는 방법도 있었군요 ㅜㅜ useRef, useMemo를 언제 어디서 써야할지 파악이 부족했나봐요... 감사합니다 |
| return () => { | ||
| listeners.delete(fn); | ||
| }; | ||
| }; | ||
|
|
||
| const unsubscribe = (fn: Listener) => { | ||
| listeners.delete(fn); | ||
| }; | ||
| // const unsubscribe = (fn: Listener) => { | ||
| // listeners.delete(fn); | ||
| // }; |
There was a problem hiding this comment.
오? 이전 unsubscirbe 함수를 주석처리하고 구현체만 옮기신 이유가 따로 있을까요?
unsubscribe 함수를 호출하게 될 경우, 함수읽는 입장에서는 구독이 종료될때 구독을 해제한다라는 것을 명확하게 알수 있지 않을까요?!
그리고 나중에 unsubscirbe도 사용할수도 있구요.
There was a problem hiding this comment.
저는 그냥 바로 return할 생각이었는데 나중에 unsubscribe를 사용하려면 다시 구현을 해놔야겠네요 ㅎㅎ !
|
LGTM!!!!!!!!!!!!!!!!!!!!!! |
| </ToastStateContext> | ||
| </ToastActionsContext> | ||
| ); | ||
| }); |
There was a problem hiding this comment.
수현님~~ 내부에서 사용된 메모이제이션 훅들을 직접 만든 useMemo, useAutoCallback을 이용해 리팩토링 해보시는 건 어떨까요?
훅 직접 만드셨는데 직접 사용하고 평가해보시면 좋을 것 같습니다 !
| return true; | ||
| } | ||
| return true; | ||
| } |
There was a problem hiding this comment.
1depth까지 순회하며 비교하는 로직은 shallowEquals 함수와 동일하고, 2depth이상부터 비교만 재귀적으로 deepEqauls함수를 사용하시니, shallowEquals 함수 사용으로 불필요한 로직을 제거하고 가독성을 높이면서 리팩토링 해보시는 건 어떨까요??
과제 체크포인트
배포 링크
https://suhyeon57.github.io/front_6th_chapter1-3/
기본과제
equalities
hooks
High Order Components
심화 과제
hooks
context
과제 셀프회고
기술적 성장
useMemo를 처음에는 이렇게 구현했는데, 매 렌더마다 실행되는 오류를 가지고 있었다. 의존성에 상관없이 실행되니 useMemo의 역할을 하지 못하는 상황이 발생
다시 제대로 구현
useShallowState.js
이 코드에서 타입을 명확하게 지정해서 타입스크립트 오류를 방지 위해 아래처럼 변경
ToastProvider.tsx
처음 시도한 코드는 useCallback과 useMemo를 설정해줬다. 하지만, Context value 객체가 바뀌면 하위 컴포넌트가 리렌더링 되는 현상이 발생했다.
두 번째 방법으로 useRef로 show/hide를 고정해도, state가 바뀌면 Context value가 바뀌어 리렌더링 되는 문제점을 발견했다.
그래서 action과 state Context를 분리해야 하며,
하위 컴포넌트가 action만 구독하면 show/hide가 변하지 않는 한 리렌더링되지 않는 것을 알게 되었다.
state는 토스트 메시지 변화에만 영향을 주도록 분리하고, (state는 메세지가 바뀔 때를 위해 분리를 해주어야 한다.)
하위 컴포넌트들은 action 요소만 구독을 하기 때문에 show와 hide가 변하지 않으면 다시 렌더링이 되지 않는다.
수정 코드
자랑하고 싶은 코드
아직은 없는 것 같습니다. 리팩토링을 통해 제 코드로 다시 만들 생각입니다.
개선이 필요하다고 생각하는 코드
이번 과제를 통해 실무에서 성능 최적화를 위한 훅 사용법을 더 명확히 이해할 수 있었습니다. 특히, 어떤 상황에서 어떤 훅을 사용해야 하는지를 고민해보는 계기가 되어 좋았습니다.
하지만 아직도 훅들이 언제, 어디서 적절하게 사용되는지에 대한 감각은 부족하다고 느꼈습니다.
이를 보완하기 위해 벨로그에 직접 정리하며 복습할 예정입니다. 이러한 과정을 통해 실무에 더 효과적으로 적용할 수 있을 것이라 기대하고 있습니다.
학습 효과 분석
ToastProvider.tsx
(useContext에 대해)
context는 props drilling 없이 컴포넌트 트리 전체에 데이터를 제공
--> 초기값 설정 시 provider 없이 사용할 수 있다.
초기값 없이 사용할 시 provider 컴포넌트를 생성하고, 커스텀 훅에서 예외 처리를 해줘야 한다.
현재 코드에서 provider 컴포넌트가 없는 이유는 초기값을 설정해주었기 때문이다.
커스텀 훅
--> 자주 사용하는 로직을 커스텀 훅으로 묶어서 사용한다. 외부에서도 사용할 수 있다.
커스텀 훅으로 사용했기 때문에 외부에서도 아래처럼 사용이 가능하다.
과제 피드백
React Hook을 실무에서 사용할 때에는 제대로 알지 못하고 쓰는 기분이 들었는데, 어떤 부분에서 렌더링이 최적화 되는지 깊게 파악할 수 있어서 좋았습니다.
그리고, 과제에서 다룬 내용을 실무에도 바로 적용해 보았습니다.
기존 윈도우 크기 추적 로직을 useSyncExternalStore를 활용해 리팩토링했고, 보다 안정적이고 예측 가능한 방식으로 개선할 수 있었습니다.
학습 갈무리
리액트의 렌더링이 어떻게 이루어지는지 정리해주세요.
React의 렌더링은 Virtual DOM(가상 DOM) 을 기반으로 동작합니다. 컴포넌트의 상태(state)나 props가 변경되면, React는 변경된 값을 기반으로 새로운 Virtual DOM을 생성합니다.
이후 이전 Virtual DOM과 새 Virtual DOM을 비교(diff 알고리즘)하여 변경된 부분만 찾아 실제 DOM에 최소한으로 반영합니다. 이 과정을 통해 DOM 조작의 비용을 줄이고 성능을 최적화합니다.
또한 React에서는 useMemo, useCallback, React.memo 등과 같은 메모이제이션 기법을 통해 불필요한 렌더링을 줄일 수 있습니다.
그리고 useEffect 등의 Hook을 통해 렌더링 이후의 사이드 이펙트 처리를 제어할 수 있으며, 이 역시 렌더링 흐름에서 중요한 역할을 합니다.
메모제이션에 대한 나의 생각을 적어주세요.
메모이제이션은 동일한 연산이 반복될 때 성능을 최적화할 수 있는 중요한 기법이라고 생각합니다. 이를 사용하지 않으면 불필요한 렌더링이나 계산이 발생할 수 있고, 결과적으로 앱 성능에 영향을 줄 수 있습니다.
특히 useMemo는 계산 비용이 큰 연산 결과를 캐싱하는 데 유리해서, 단순히 값을 보존하는 useRef보다 성능 측면에서 더 적절할 수 있습니다.
실제로 ToastProvider 과제를 진행하면서 메모이제이션을 통해 리렌더링을 줄이고 성능을 향상시킬 수 있다는 점을 체감했습니다.
컨텍스트와 상태관리에 대한 나의 생각을 적어주세요.
Context는 props drilling 없이 컴포넌트 트리 전체에 데이터를 전달할 수 있게 해주는 기능입니다. 이를 통해 상위 컴포넌트에서 하위 컴포넌트로 일일이 props를 전달하지 않아도 되므로 코드가 간결해지고 유지보수가 쉬워집니다.
또한 Context와 상태관리 도구를 함께 사용하면 전역 상태를 효과적으로 관리할 수 있습니다. 적절하게 분리하지 않으면, 하위 컴포넌트가 불필요하게 자주 리렌더링되는 문제가 발생할 수 있습니다.
따라서 Context는 단순히 데이터를 공유하는 역할을 넘어, 상태관리 전략과 함께 사용함으로써 리렌더링을 최소화하고, 더 효율적인 앱 구조를 만드는 데 큰 도움이 된다고 생각합니다.
리뷰 받고 싶은 내용
저는 아래 코드에서 createActions(dispatch)로 만든 show, hide 함수를 useRef로 감싸서 리렌더링과 무관하게 고정된 값으로 유지하고, 이후 useCallback 안에서 사용하고 있습니다. 이런 방법을 통해 useCallback에서 의존성 배열을 빈 배열로 두어도 lint 경고가 생기지 않는데, 이 방법을 사용해도 되는지 궁금합니다.
(현재는 팀원들의 도움으로, useMemo, useCallback 방법으로 변경하였습니다.)
이번 과제를 통해 React Hook이 어떤 역할을 하고, 어떻게 사용되는지에 대해 감을 잡을 수 있었습니다. 하지만 아직도 어떤 상황에서 어떤 Hook을 써야 하는지, 그리고 성능 최적화를 위해 어떤 판단 기준을 가져야 할지는 여전히 모호하게 느껴집니다.
혹시 Hook을 더 잘 이해하고 적재적소에 활용하기 위해 추천하시는 학습 방법이나 연습 방식이 있다면 알려주시면 감사하겠습니다!