[2팀 박소연] Chapter 1-3. 프레임워크 없이 SPA 만들기#6
Open
soyalattee wants to merge 19 commits intohanghae-plus:mainfrom
Open
[2팀 박소연] Chapter 1-3. 프레임워크 없이 SPA 만들기#6soyalattee wants to merge 19 commits intohanghae-plus:mainfrom
soyalattee wants to merge 19 commits intohanghae-plus:mainfrom
Conversation
…useCallback 사용하여 함수의 참조는 고정함
… 콜백함수로 감싸서 구현함. 내부에서는 setValue를 shallowEquals로 기존값과 변경된 값을 비교하여 업데이트한다.
…omponent를 내보내 재렌더링시키거나 null을 보냄(null을 보내면 렌더링이 안되는거 맞겠지..?)
bebusl
reviewed
Jul 24, 2025
| const hideAfter = useMemo(() => debounce(hide, DEFAULT_DELAY), [hide]); | ||
|
|
||
| const showWithHide: ShowToast = (...args) => { | ||
| const showWithHide: ShowToast = useAutoCallback((...args) => { |
There was a problem hiding this comment.
useAutoCallback을 쓰면 되는군요
넘나 익숙하게 useCallback썼었는데
Comment on lines
6
to
16
| //2. MemoizedComponent | ||
| const MemoizedComponent = (props: P) => { | ||
| //1. prevRef | ||
| const prevRef = useRef<P | null>(null); | ||
|
|
||
| if (!equals(prevRef.current, props)) { | ||
| prevRef.current = props; | ||
| return Component(props); | ||
| } | ||
| return null; | ||
| }; |
There was a problem hiding this comment.
로직을 변수안에 담아두신 것이 어떤 로직인지 한 눈에 들어와서 좋네요!✨
…\#discussion_r2228383737 state 초기 상태값 객체에서 함수로 변경하여 렌더링시 객체의 재생성 막음
|
LGTM 👍 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
과제 체크포인트
배포 링크
https://soyalattee.github.io/front_6th_chapter1-3/
기본과제
equalities
hooks
High Order Components
심화 과제
hooks
context
과제 셀프회고
기술적 성장
이번 과제를 통해 관성적으로 사용하던 React 훅들을 내부 동작과 원리를 이해하게되었습니다.
앞으로는 React 훅을 사용할때 더 의도를 명확하게 사용할 수 있을것 같습니다.
“DOM을 참조하거나, 렌더링과 무관하게 값을 유지하는 용도” 정도로만 이해하고, 암기식 패턴처럼 사용하고 있었습니다.
이번 과제를 통해 useRef가 렌더링 사이에서도 값을 유지하는 객체로 React 내부 상태로 인식되지 않아 리렌더링을 발생시키지 않는다는 점을 이해하게 되었고, useRef에 대한 활용범위를 넓힐 수 있었습니다.
useState 의 내부 구조를 탐색해보며 상태가 클로저와 배열을 기반으로 순서 기반으로 관리되는 구조라는걸 알 수 있었습니다.
특히 해당 아티클을 참고하며
setState호출 시, 컴포넌트 함수가 다시 실행되며 상태 값을 업데이트하는 흐름을 이해했습니다.그리고 React의 Hook이 왜 그런 방식으로 설계 되었는지에 대한 이해를 얻을 수 있었습니다.
이를 통해 단순 동작하는 코드를 넘어 '이 방식이 성능에 어떤 영향을 줄까?'를 고민하고 설계할 수 있는 개발자가 될 수 있었습니다.
자랑하고 싶은 코드
팀원들과 코드리뷰를 진행하며 트러블슈팅한 과정이 만족스러워서 공유해보려고합니다.
memo.ts
<문제 발견>
진희님에게 memo.ts 코드에 관해 다음과 같은 피드백을 받았습니다.
다시 생각해보니 문제가 있는 코드였고, 해당 memo 를 사용해 테스트하기 위해 TestApp 파일을 만들었습니다.
<원인>
예상대로 첫 렌더링후 재렌더링이 발생하며 같은 prop이 들어가자, null이 반환되어 자식 컴포넌트가 사라졌습니다.
<문제 해결>
component를 ref에 저장해서 관리해야 재렌더링이 발생하지 않겠구나 라고 생각되어 코드를 수정했습니다.
prevRef 에 props와 component를 객체로 만들어 동시에 저장하였고, 기존 props와 변경된 props를 비교하여, 변경이 일어났을때에만
prevRef.current.component(props)를 반환했습니다.하지만 이 코드에도 문제가 있었습니다.
해당 코드는 함수를 반환하며 매번 컴포넌트를 호출하게되어, 반환할때마다 새롭게 렌더링이 되고있었습니다.
'컴포넌트의 렌더링 결과'를 저장하기 위해 다시 코드를 수정했습니다.
(TO-BE)
useRef에 저장할 값의 타입을 ReactNode로 하고, Copoment(props) 값을 저장하여 렌더링 결과를 캐싱할 수 있었습니다.
수정 커밋: d648629
shallowEquals&deepEquals
<문제 발견>
도은님에게 object와 배열 비교문 관련하여 피드백 받았습니다.
<원인> (AS-IS)
Object 비교시에 배열도 처리됨으로 마지막에 작성한 배열비교 코드는 작동하지 않고 있었습니다.
하지만 해당 코드는 문제가 생길 수 있습니다.
예를들어,
객체와 배열을 비교할 경우, 배열의 키 값도 0, 1 순으로 들어가게 됩니다.
이런 케이스에 의도치 않은 true 가 반환될 수 있습니다.
<문제 해결>
우선 배열비교를 객체 비교보다 먼저 할 수 있도록 했습니다.
그리고 객체를 비교할때 체크하던 null 체크도 위로 올려 가독성을 개선했습니다.
(TO-BE)
팀원들과의 코드리뷰를 통해 제가 놓치고 있던 에러 케이스를 빠르게 발견하고,
더 명확하고 읽기 쉬운 구조로 리팩토링할 수 있었습니다.
학습 효과 분석
앞서 언급했듯이, 단순 동작하는 코드를 넘어 '이 방식이 성능에 어떤 영향을 줄까?'를 고민하고 설계할 수 있는 개발자로 성장 할 계기가 되었습니다.
이번에 구현해본
memo와useCallback을 활용한 렌더링 최적화 기법은 평소에 잘 적용하지 않고 개발했던 부분 이였습니다.앞으로는 실제 업무에서 자주 사용하는 컴포넌트들에 직접 적용해보고, 렌더링 빈도 변화나 성능 차이를 DevTools로 분석해보며 효과를 검증해보려고 합니다.
과제 피드백
이번 과제는 React에서 Hook의 본질을 이해하고 직접 구조를 따라는 식으로 학습 경험을 쌓아갈 수 있어 좋았습니다.
그동안 "작동은 되지만 왜 이렇게 써야 하지?"라고 생각했던 부분들이 직접 구현하고 실험하면서 "이런 구조여서 이렇게 동작하는구나.."라고 체득되는 순간들이 많아 좋았습니다.
그런데, 제가 그냥 '테스트 코드 통과하네' 하고 넘어간 부분이 있었는데 (memo.js, deepMemo.js)
목요일 팀원들과 코드리뷰를 한 덕에 '어..이거 잘못짰네...?' 라고 알아챈 부분이 있었습니다.
제가 memo.js에서 null을 리턴하고 있었어요 ㅎㅎ..
해당 커밋: 8aacb39
basic.test.tsx의 '직접 만든 memo' 테스트코드에서 호출 횟수는 체크하나, null 관련 체크는 없어서 통과되었나봐요 하핫
학습 갈무리
리액트의 렌더링이 어떻게 이루어지는지 정리해주세요.
React의 렌더링은 크게 초기 렌더링과 리렌더링 두 단계로 나눌 수 있다.
1. 초기 렌더링
** 15이하 버전에서는 vDOM 트리를 재귀적으로 순회하는 방식
2. 리렌더링
다음과 같은 경우 리렌더링이 발생한다.
useState,useReducer등으로 상태 업데이트useContext로 구독 중인 context 값이 변경됨forceUpdate()호출리렌더링이 일어나면, React는 이전 vDOM과 새로운 vDOM을 비교(diffing)하고 변경된 부분만 real DOM 에 업데이트한다.
→ 이를 Reconciliation이라고 부른다.
컴포넌트 함수 자체는 리렌더링 시마다 다시 실행되지만, 실제 DOM 업데이트는 변경된 부분만 수행되는것 (부분 렌더링)
3. 자식 컴포넌트도 무조건 리렌더링 될까?
기본적으로 부모 컴포넌트가 리렌더링되면 자식도 재실행된다. 하지만 자식 컴포넌트가 props가 바뀌지 않았다면 굳이 리렌더링할 필요가 없다.
→ 이를 방지하기 위한 최적화 방법들이 있다.
React.memo(Component)useCallback(fn, deps)useMemo(valueFn, deps)useRef정리
메모이제이션에 대한 나의 생각을 적어주세요.
메모이제이션이란, 컴퓨터가 이미 계산한 결과를 저장해뒀다가 같은 계산이 필요할 때 재사용하는 최적화 기법이다. React에서 메모이제이션이 필요한 이유는 다음과 같다.
1. 리액트 렌더링 시 매번 모든 코드를 재실행한다
React에서 렌더링은 컴포넌트 함수를 매번 다시 호출하는 과정이다. 상태가 바뀔 때마다 React는 컴포넌트 안에 있는 코드를 위에서부터 아래로 전부 실행한다.
즉, 매 렌더링마다 다음의 모든 과정이 다시 일어난다:
만약 렌더링 과정에 아래와 같은 무거운 연산이 포함되어 있다면 어떨까?
예: (AS-IS)피보나치 계산
React에서 뜬금없이 피보나치를 돌일 일은 없을것 같지만, 이렇게 매 렌더링마다 동일한 계산이 반복되면 성능이 크게 떨어진다.
이때 사용하는 최적화 방법이 바로 메모이제이션이다.
(TO-BE)적용 예(useMemo)
이제
num이 변경될 때만 피보나치가 다시 계산되므로 효율적으로 동작한다!2. 불필요한 리렌더링을 방지하고 싶을때
컴포넌트가 받는 props가 바뀌지 않으면 이전 렌더링 결과를 재사용하여 불필요한 리렌더링을 방지하는 메모이제이션 기법이다.
부모 컴포넌트가 리렌더링될 때 자식 컴포넌트가 props의 변경이 없는데도 불필요하게 다시 렌더링되는 경우에 사용할 수있다.
props가 참조로 전달된 함수나 객체일 경우, 참조가 바뀌지 않도록
useCallback이나useMemo로 미리 처리해줘야 효과적이다.예: (AS-IS) 부모가 자식에게 props를 전달할 때
부모가 리렌더링될 때마다 자식 컴포넌트는 변경된 props가 없음에도 불구하고 계속 리렌더링된다.
(TO-BE)적용 예 (memo)
props가 변경되지 않는 한, 자식 컴포넌트는 리렌더링되지 않는다.
하지만 전달하는 props가 함수나 객체라면 참조 변경에 주의해야 한다.
3. 함수 참조를 유지해야 할 때
메모이제이션은 무거운 연산 외에도 함수나 객체의 참조(reference)를 유지하여 불필요한 리렌더링을 막을 때 유용하다.
특히 React에서 자주 발생하는 문제는 자식 컴포넌트에 콜백 함수를 넘길 때마다 참조가 변경되어 불필요하게 자식이 리렌더링되는 현상이다.
이런 경우에
useCallback으로 함수의 참조를 고정할 수 있다.예: (AS-IS) 부모가 자식에게 함수를 전달
이 경우, 부모 버튼을 누를 때마다 자식은 변경되는 내용이 없지만 함께 매번 리렌더링 된다.
(TO-BE) 적용 예 (useCallback)
이제 자식 컴포넌트는 첫 렌더링 이후 부모 컴포넌트가 리렌더링 되어도 더 이상 리렌더링되지 않는다.
정리
언제 메모이제이션을 사용할까?
useMemo)useCallback,memo)컨텍스트와 상태관리에 대한 나의 생각을 적어주세요.
1. 컨텍스트(Context)
처음엔 React의 Context는 컴포넌트 간에 값을 전달할 때 props drilling (props를 여러 컴포넌트를 거쳐서 전달하는 비효율적인 상황) 을 피하기 위해 사용된다고 막연히 생각했다.
주로 앱 전역에서 공유해야 하는 상태(예: 언어 설정, 테마 등)를 관리할 때 적절하다.
예: (AS-IS) props drilling이 발생하는 상황
(TO-BE) context 적용
이렇게 하면 props를 통해 테마를 넘기지 않고도 어떤 컴포넌트에서도 테마에 접근할 수 있다.
2. 상태관리
앱의 규모가 커지고, 복잡해질수록 상태의 범위가 넓어지며 상태관리의 중요성이 높아진다.
상태관리가 없다면 전역 상태를 효율적으로 관리하기 어렵고 같은 상태를 여러 컴포넌트가 사용하거나 서로 멀리 떨어진 컴포넌트들이 상태를 공유하는 상황에서 관리가 어렵게 된다.
이런 문제를 해결하기 위해 Redux, Zustand, Recoil 같은 상태 관리 라이브러리가 등장했다!
정리
리뷰 받고 싶은 내용
ToastProvider 를 구현할 때, useCallback과 useAutoCallback 중에 어떤걸 쓰는게 좋을까 라는 고민이 있었습니다.
useCallback만으로 충분히 최적화가 가능할것 같은데, 이때 useAutoCallback을 쓰면 좋다 라는 케이스가 어떤경우가 있을지 궁금합니다.