과제 체크포인트
배포 링크
https://ckdwns9121.github.io/front_6th_chapter1-3/
기본과제
equalities
- shallowEquals 구현 완료
- deepEquals 구현 완료
hooks
- useRef 구현 완료
- useMemo 구현 완료
- useCallback 구현 완료
- useDeepMemo 구현 완료
- useShallowState 구현 완료
- useAutoCallback 구현 완료
High Order Components
- memo 구현 완료
- deepMemo 구현 완료
심화 과제
hooks
- createObserver를 useSyncExternalStore에 사용하기 적합한 코드로 개선
- useShallowSelector 구현
- useStore 구현
- useRouter 구현
- useStorage 구현
context
- ToastContext, ModalContext 개선
과제 셀프회고
기술적 성장
이번 과제를 진행하면서 기존에 사용하였던 React 훅들이 어떤 매커니즘으로 만들어졌고, 왜 이렇게 동작을 하는지 코드레벨에서 깊이있게 이해할 수 있었던 과제였습니다.
특히 메모이제이션에 대해서 더 깊이 고민해볼 수 있었습니다. 예전에는 useMemo, useCallback, useRef 등 React에서 제공하는 훅들을 단순히 "최적화를 위한 도구" 정도로만 생각했었는데, 이번 과제를 통해
- 각각의 훅이 내부적으로 어떤 동작 원리로 작동하는지
- 불필요한 리렌더링을 막기 위해 어떤 비교 전략(shallowEquals, deepEquals)이 사용되는지
- 외부 스토어/ 라우터와의 연동에서 useSyncExternalStore 훅과 연동한 구독, 알림 시스템이 어떻게 동작하는지
좀 더 깊이있게 학습할 수 있었습니다.
useSyncExternalStore 훅에 대한 학습
공부하다 보니 재미있는 사실을 발견했습니다. React가 성능과 사용자 경험을 높이기 위해 동시성 렌더링을 도입했는데, 아이러니하게도 이게 상태가 찢어지는(Tearing) 문제를 만들어냈더라고요. 즉, UX 문제를 해결하려고 도입한 기능이 새로운 문제를 만든 셈이죠. 그걸 또 해결하기 위해 등장한 게 바로 useSyncExternalStore라는 훅이었고요.
A 문제를 해결하려고 B를 도입했더니, 또 다른 문제가 생겨서 C를 만들어낸 흐름이 React의 진화 과정 자체가 하나의 문제 해결 여정처럼 느껴져서 정말 흥미로웠습니다.
React는 단순히 새로운 기능만 쌓아가는 게 아니라 기존의 구조적 문제를 하나하나 개선해가며 점진적으로 아키텍처를 정제해가는 프레임워크라는 그런 진화과정을 엿볼 수 있었던것 같습니다.
useSyncExternalStore훅 관련된 내용은
React 18부터 도입된 useSyncExternalStore 훅 살펴보기라는 포스팅에 정리하였습니다.
useAutoCallback와 같은 유연한 커스텀훅을 설계하는 사고방식 습득
과제를 진행하면서 제일 인상깊었던 코드는 useAutoCallback과 같은 커스텀 훅이였습니다.
이전까지는 useCallback만 사용하면 함수의 재생성을 막을 수 있다고 막연히 생각했지만, 실제로는 의존성 배열에 따라 최신 상태를 참조하지 못하는 문제가 종종 있습니다. (의존성 누락).
그 문제를 useRef와 useCallback을 조합해서 항상 최신 함수를 참조하면서도 함수의 참조값은 변하지 않게 만드는 패턴이 매우 유용하다는 것을 직접 구현하며 체감할 수 있었습니다.
useAutoCallback은 아래와 같은 과정으로 구현하였습니다.
1. useRef로 최신 함수 보관
const callbackRef = useRef<T>(fn);
callbackRef.current = fn;
- useRef는 컴포넌트가 리렌더링되어도 동일한 ref 객체를 반환합니다.
- callbackRef.current에 항상 최신의 fn을 할당합니다. 즉, 컴포넌트가 리렌더링될 때마다 최신의 fn이 ref에 저장됩니다.
- 이로써 콜백 함수 내부에서 항상 최신의 함수를 참조할 수 있게 구현했습니다.
2. useCallback으로 함수 참조를 고정
return useCallback((...args: Parameters<T>) => {
return callbackRef.current(...args);
}, []) as T;
- useCallback은 의존성 배열이
[](빈배열)이므로 최초 마운트 시에만 콜백을 생성하고, 이후에는 동일한 함수 참조를 반환합니다. - 하지만 이 콜백 내부에서 직접 fn을 참조하지 않고 ref를 통해 접근하게 만들었습니다.
- 즉 콜백 함수의 참조는 고정되어 있지만 내부에서 실행되는 fn은 항상 최신입니다.
3. 최종 구현된 코드
import type { AnyFunction } from "../types";
import { useCallback } from "./useCallback";
import { useRef } from "./useRef";
export const useAutoCallback = <T extends AnyFunction>(fn: T): T => {
const callbackRef = useRef<T>(fn);
callbackRef.current = fn;
return useCallback((...args: Parameters<T>) => {
return callbackRef.current(...args);
}, []) as T;
};
이 구현으로 인해서 useCallback의 단점인 state closure문제를 우회하면서도 불필요한 함수 재생성 없이 항상 최신상태를 참조할 수 있는 커스텀 훅을 구현하였습니다. 이 패턴을 활용해서 토스트에서 showWithHide 함수에 적용하여 타이머 리셋 문제를 개선할 수 있었습니다.
이렇게 상황에 따라 유연하게 필요한 훅을 재조합하거나 필요시 새롭게 구현할 수 있는 사고방식을 기르게 되었습니다.
또한, 토스트 UI를 Context 기반으로 구현한 ToastProvider를 리팩토링하면서 불필요한 리렌더링 방지와 관심사 분리의 중요성을 직접 경험해볼 수 있었습니다.
초기에는 상태(state)와 액션(show/hide)을 하나의 Context에 통합한 구조를 사용했는데, 이 경우 message가 변경되면 액션만 사용하는 컴포넌트도 불필요하게 리렌더링되는 이슈가 있었습니다. 이를 해결하기 위해 다음과 같은 리팩토링을 진행하였습니다.
1. 상태와 액션을 별도의 Context로 분리
// 상태만 담는 Context
const ToastStateContext = createContext({ message: "", type: "success" });
// 액션만 담는 Context
const ToastActionContext = createContext({ show: () => {}, hide: () => {} });
// 소비하는 쪽
export const useToastState = () => useContext(ToastStateContext);
export const useToastCommand = () => useContext(ToastActionContext);
이전에는 하나의 Context에 message, show(), hide()등 모든걸 넣었는데 상태와 액션을 분리해 필요한 값만 구독해서 불필요한 렌더링을 방지하였습니다.
2. value 객체는 useMemo로 메모이제이션
기존 코드는 value에 객체 리터럴을 바로 넘기는 방식이였습니다.
<ToastActionContext.Provider value={{ show, hide }}>
이 객체는 매번 렌더링할 때 새로운 객체로 생성됩니다. 즉
{ show, hide } !== { show, hide } // 참조값 다름
이렇게 값은 같아도 참조하고 있는 메모리 주소가 다르기 때문에 이 컨텍스트를 사용하는 모든 하위 컴포넌트가 리렌더링이 되는 문제가 있었습니다.
그래서 useMemo를 활용해서 show와 hide 함수가 불필요하게 재생성되지 않도록 고정시켰습니다.
const { show, hide } = useMemo(() => createActions(dispatch), [dispatch]);
3. showWithHide를 useAutoCallback으로 고정된 참조 + 최신상태를 보장하게 구현
위에서 언급한 useAutoCallback을 통해 아래와 같이 리팩토링하였습니다.
const showWithHide = useAutoCallback((message: string, type: ToastType) => {
show(message, type);
hideAfter(); // debounce(hide)
});
이 함수는 다음과 같은 이점을 가집니다.
- 외부에 전달되는 showWithHide는 항상 동일한 참조값을 유지하므로, useMemo 등에서 안전하게 사용 가능합니다.
- 내부에서 ref를 통해 최신
show,hideAfter를 참조하므로 클로저에 의한 stale 상태 문제를 방지할 수 있습니다. - 결과적으로
show()를 연속으로 호출한다 해도debound가 정상적으로 리셋 되면서 가장 마지막 호출 기준으로 토스트 메시지가 닫히게 됩니다.
학습 효과 분석
1. 리액트는 왜 얕은비교 방식을 채택했을까?
React는 상태 비교에서 기본적으로 **얕은 비교(shallow comparison)**를 사용합니다. 이는 단순히 "성능 때문에 그렇다"는 얘기보단 어떤 철학적인 설계에 가깝다고 생각했습니다.
- 깊은 비교를 하게되면 객체가 중첩될수록 비교 비용이 기하급수적으로 늘어납니다.
- 반면 얕은 비교는
===연산 및 한 레벨의 비교로 끝나기 때문에 예측 가능하고 빠릅니다. - React는 불변성을 지키는 방식으로 상태를 다루도록 유도하는데 얕은 비교만으로 상태 변경 여부를 판단할 수 있게 만들었습니다.
즉 React는 개발자에게 불변성 유지를 책임지게 하고, 프레임워크는 그 위에서 가볍고 빠른 비교를 제공하는 방식을 택했다고 생각합니다. 이로 인해 컴포넌트 리렌더링 판단, memo, useMemo, useCallback 같은 훅의 최적화가 모두 얕은 비교를 기준으로 설계되었다고 합니다.
2. useAutoEffect, useAutoMemo와 같은 훅은 만들 수 없을까?
이번 과제에서 useAutoCallback을 구현하면서 참조는 고정하면서 항상 내부에서 최신상태를 받아올 수 있는 훅도 만들 수 있지않을까? 그것을 만들면 완전 혁신아니야!? 라는 기대도 했습니다.
이 아이디어를 기반으로 useAutoEffect 같은 훅을 상상해봤습니다. 예를 들어, 의존성 배열 없이도 최신 상태를 반영하는 effect 훅을 만들 수 있다면 매번 의존성 배열을 일일이 나열하지 않아도 돼서 굉장히 편리할 것 같았습니다.
그래서 아래와 같은 시도를 했습니다.
const useAutoEffect = (effectFn: () => void) => {
const ref = useRef(effectFn);
ref.current = effectFn;
useEffect(() => {
return ref.current();
}, []);
};
문제점
- 보기엔 ref.current가 항상 최신 effectFn을 참조하므로 괜찮아 보이지만, 실제로는 이 useEffect는 컴포넌트 마운트 시 단 한 번만 실행됩니다
- 즉, 이후 effectFn이 바뀌어도 다시 실행되지 않게 때문에 때문에 ref.current가 최신이든 아니든 다시 실행되지 않는 문제점이 있었습니다.
마찬가지로 useAutoMemo도 만들어볼 수 있을 것 같았지만
const useAutoMemo = <T>(factory: () => T): T => {
const ref = useRef(factory);
ref.current = factory;
return useMemo(() => ref.current(), []);
};
문제점
- useMemo(..., [])는 초기 마운트 시 한 번만 실행되며 그 이후에는 ref가 최신이더라도 factory는 다시 평가되지 않습니다.
- 결국 매번 팩토리함수를 다시 실행해야하는데 이는 메모이제이션이 아니라 매번 새로운 결과를 재생성하는 것이기 때문에 메모이제이션의 목적을 달성하지 못했습니다.
이 훅들을 구현해보면서 useAutoCallback은 언제 실행될지를 사용자가 제어하기 때문에 최신 상태 참조에 의미가 있었지만, 제가 시도하려 했던 useAutoEffect, useAutoMemo는 React가 정한 타이밍에 실행되는 훅이기 때문에 내부에 최신 참조(ref)가 있어도 그 타이밍이 고정되어 있어서 최신 상태를 반영할 수 없는 구조적인 한계가 있다는것을 깨달았습니다.
3. useShallowSelector의 동작 원리 분석 useShallowSelector의 동작 원리가 잘 이해 되지 않아서 천천히 디버깅해보며 학습한 내용을 정리하였습니다.
흐름 상세 설명
- 컴포넌트가
useStore(또는useRouter)를 호출할 때, selector 함수를 함께 전달합니다. useStore내부에서useShallowSelector(selector)를 호출하여, selector 기반의 shallowSelector 함수를 만듭니다.useSyncExternalStore를 통해 store의 subscribe로 상태 변화를 구독합니다.- store의 상태가 바뀌면, subscribe에 등록된 콜백이 실행되어 컴포넌트가 다시 shallowSelector(state)를 호출합니다.
- shallowSelector는 selector(state)로 값을 뽑고, 이전 값과 shallowEquals로 비교합니다.
- 값이 다르면 prevSelectedRef를 갱신하고 새 값을 반환하여 컴포넌트가 리렌더링됩니다.
- 값이 같으면 이전 값을 그대로 반환하여 불필요한 리렌더링을 방지합니다.
이 흐름을 시각화 하였습니다.
sequenceDiagram
participant 컴포넌트
participant useStore
participant useShallowSelector
participant store
participant shallowEquals
컴포넌트->>useStore: selector 전달하며 호출
useStore->>useShallowSelector: selector 전달
useStore->>store: subscribe 등록
useStore->>store: getState()로 상태 조회
useStore->>useShallowSelector: shallowSelector(state) 호출
useShallowSelector->>shallowEquals: 이전 값과 selector(state) 결과 비교
alt 값이 다름
shallowEquals-->>useShallowSelector: false
useShallowSelector-->>useStore: 새 값 반환 (prevSelectedRef 갱신)
useStore-->>컴포넌트: 새 값 전달, 리렌더링 발생
else 값이 같음
shallowEquals-->>useShallowSelector: true
useShallowSelector-->>useStore: 이전 값 반환
useStore-->>컴포넌트: 이전 값 전달, 리렌더링 없음
end
store-->>useStore: 상태 변경 시 notify (구독자 콜백 호출)
useStore->>useShallowSelector: shallowSelector(state) 재호출 (이전과 동일 흐름 반복)
4. 실제 ProductList에 메모이제이션을 도입하여 성능 측정
이 컴포넌트는 무한 스크롤 구조로, 스크롤이 일정 위치에 도달할 때마다 새로운 데이터를 계속 fetching 해오는 구조입니다.
products 상태가 갱신될 때마다 ProductList가 리렌더링되고, 그에 따라 내부의 ProductCard들도 전부 리렌더링 되는 문제가 있었습니다.
성능 측정 시나리오
- 무한스크롤을 10번해서 데이터 받아오기 (current=10이 될때까지)
- React Profier를 이용해 이 과정을 측정합니다.
| 항목 | 메모이제이션 전 | 메모이제이션 후 |
|---|---|---|
| 이미지 | ||
| 최대 렌더링 시간 | 13.5s for 96.2ms – 프레임 드랍 위험 있음 | 12.3s for 27.4ms – 개선됨, 여전히 최적화 여지 있음 |
| 렌더링 방식 | ProductCard가 매번 전부 리렌더링됨 (key=...) | MemoizedComponent로 감싸진 컴포넌트들은 대부분 <0.1ms, 재렌더링 회피됨 |
| 렌더링 시간 분포 | 대부분 0.3~0.5ms씩 소모되는 카드들이 20개 이상 → 누적 비용 큼 | 대부분 <0.1ms로 스킵되며 누적 비용이 대폭 감소 |
| ProductList | 여전히 매번 리렌더링 (state 변경 영향) | 불필요한 리렌더링 없음 |
| 성능 요약 | memo 미적용 시, 무한스크롤에 따른 전체 컴포넌트 재렌더링 발생, FPS 저하 | memo 적용으로 동일 props인 컴포넌트 재사용 → CPU 사용량 및 프레임 시간 감소 |
메모이제이션을 적용하지 않았을 때는 ProductCard가 props가 같음에도 모두 다시 렌더링되어 96.2ms에 달하는 프레임이 발생했고,
이는 사용자가 스크롤을 내릴 때 프레임 드랍이나 버벅임을 느낄 수 있는 수준입니다.
반면, memo를 적용한 후에는 동일 props를 가진 카드들이 렌더링을 건너뛰게 되면서 전체 렌더링 시간은 27.4ms로 약 3.5배 이상 감소했습니다. 특히 각 ProductCard 컴포넌트가 <0.1ms 수준으로 최적화되어 있어 불필요한 리렌더링이 일어나지 않았음을 확인할 수 있었습니다.
메모이제이션을 적용한 코드는 여기서 확인할 수 있습니다.
과제 피드백
React의 훅을 직접 단계별로 구현해보면서 동작원리를 자세히 익힐 수 있어서 좋았습니다.
학습 갈무리
리액트의 렌더링이 어떻게 이루어지는지 정리해주세요.
리액트의 렌더링은 어떻게 이루어질까?에 정리하였습니다.
메모이제이션에 대한 나의 생각을 적어주세요.
React에서 useMemo, useCallback, memo와 같은 메모이제이션 도구는 성능 최적화를 위한 수단이지, 반드시 적용해야 하는 규칙은 아니라고 생각합니다.
개발자들 사이에서 자주 나오는 얘기 중 하나는 다음과 같은 토론입니다.
"일단 다 적용하고 보자" vs "성능 이슈가 생겼을 때 최적화하자"
저는 이 중에서 성능 이슈가 발생했을 때 최적화하자에 가깝습니다.
실제로 React useMemo의 공식문서에도 아래와 같은 내용이 있습니다.
If your app is like this site, and most interactions are coarse (like replacing a page or an entire section), memoization is usually unnecessary. On the other hand, if your app is more like a drawing editor, and most interactions are granular (like moving shapes), then you might find memoization very helpful. Optimizing with useMemo is only valuable in a few cases
❌ 무분별한 메모이제이션의 문제점
1. 의존성 배열 실수
useMemo(() => ..., [a, b])처럼 작성했을 때, 의존성 하나만 빠져도 오래된 값을 계속 참조하게 됩니다. 특히 협업 중 리팩토링 시, 눈에 띄지 않는 버그로 발전하기 쉽습니다.
2. 디버깅과 테스트 복잡도 증가 stale value와 관련된 버그는 눈에 띄지 않기 때문에 추적이 어렵고, 테스트 코드에서도 예외적인 흐름을 만들어냅니다.
3. 불필요한 메모리 사용과 렌더링 최적화 실패 모든 것을 메모이제이션한다고 해서 항상 성능이 좋아지는 것은 아닙니다. 오히려 메모이제이션 오버헤드가 렌더링 비용보다 커질 수도 있다고 생각합니다.
따라서 무분별한 메모이제이션은 코드 품질을 떨어뜨릴 수 있으며, 선택적이고 명확한 목적 아래에서만 사용해야한다는 것이 저의 생각입니다.
✅ 내가 따르는 메모이제이션 원칙
불변성을 지켜야 하는 상황에서만 메모이제이션을 쓴다. 예: Context의 value 객체, 복잡한 계산 로직 등
렌더링 병목이 실제로 관측되는 경우에만 메모이제이션을 적용한다. 예: 수천 개의 리스트 렌더링, 연산이 무거운 경우 등
불필요한 최적화는 하지 않는다. 나중에 코드를 이해하는 사람에게는 오히려 방해 요소가 될 수 있다.
메모이제이션은 "언제든 쓰면 좋은 도구"가 아니라 "쓸 줄 아는 사람이 신중하게 선택해야 하는 칼"이라고 생각합니다.
이 관점을 바탕으로 실무나 과제에서도 메모이제이션 도구를 무분별하게 남용하지 않고, 필요한 곳에만 적절히 적용하는 습관을 가지려 하고 있습니다.
컨텍스트와 상태관리에 대한 나의 생각을 적어주세요.
많은 사람들이 React의 Context를 전역 상태 관리 도구로만 이해하지만 실제로는 의존성 주입(DI) 시스템에 더 가까운 개념이라고 생각합니다. Context는 상위 컴포넌트에서 특정 값을 설정해 하위 컴포넌트로 직접 전달할 수 있게 해주며, 이 구조는 전통적인 DI 패턴과 매우 유사합니다. 실제로 많은 라이브러리들이 이 구조를 적극적으로 활용하고 있습니다.
1. ThemeProvider
하위의 모든 Component에서 theme객체를 참조할 수 있게 해주는 구조로 전형적인 설정값 주입의 대표적인 예시입니다.
<ThemeProvider theme={myTheme}>
<App />
</ThemeProvider>
2. TanStack Query – QueryClientProvider
API 캐싱과 상태 관리를 담당하는 QueryClient를 트리 전체에 주입하여 하위 컴포넌트에서는 별도의 설정 없이 useQuery, useMutation 등의 훅을 통해 해당 클라이언트를 자동으로 참조하고 사용할 수 있게됩니다.
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
3. i18next – I18nextProvider
번역 객체(i18n 인스턴스)를 하위 컴포넌트에 주입해서 어디서든 다국어 기능을 사용할 수 있게 합니다.
<I18nextProvider i18n={i18n}>
<App />
</I18nextProvider>
이러한 패턴들은 모두 다음과 같은 공통점을 가집니다
- 상위에서 특정 객체나 설정값을 생성
- Provider로 컴포넌트 트리에 주입
- 하위에서 Hook이나 Context로 접근하여 사용
이것은 전형적인 의존성 주입 패턴이며, React의 Context가 전역 상태 관리 도구라기보다 애플리케이션의 구성 요소를 주입하는 용도에 더 적합하다는 걸 보여주는 대표적인 예시들입니다.
결론
Context는 "전역 상태 관리"보다는, "외부에서 설정값이나 서비스 객체를 주입"하는 데 더 적합한 도구다.
- 자주 바뀌는 상태를 Context로 관리하면 불필요한 리렌더링이 많아지고 성능 저하가 발생할 수 있다.
- 반면, 잘 변하지 않는 값(API client, config, 다국어 객체 등)은 Context로 주입하면 props drilling 없이 효율적으로 전달할 수 있다.
리뷰 받고 싶은 내용
Q1. 업무를 진행하실 때 메모이제이션을 다 해놓은 편인가요? 아니면 성능 이슈가 발생했을 때 적용하시나요?
Q2. 만약 성능이슈가 발생해서 메모이제이션을 적용하신 경험이 있다면 한번 소개해주시면 감사합니다!
Q3. 컨텍스트와 상태관리에 대한 코치님의 생각도 궁금합니다.
과제 피드백
안녕하세요 창준! 수고했어요~ 이번 과제는 React의 내장 훅들을 직접 구현하면서 프레임워크의 내부 동작 원리를 깊이 이해하는 것이 목표였습니다. 창준님의 PR을 보면서 정말 깊은 탐구와 열정이 느껴지는 학습을 했다는게 느껴졌네요!
단순히 과제를 완료하는 것을 넘 성능 측정과 최적화까지 직접 경험해본 부분 칭찬합니다 특히 무한 스크롤에서 메모이제이션 적용 전후의 렌더링 시간을 React Profiler로 측정하여 96.2ms에서 27.4ms로 약 3.5배 개선한 실험은 정말 훌륭했습니다. 개발은 이렇게 항상 직접 해보면서 내가 체험적으로 느껴보는게 참 중요한 학습이라고 생각하는데 정말 잘했습니다!
useSyncExternalStore의 등장 배경에 대한 이해도 인상적이었습니다. React의 동시성 렌더링이 상태 찢어짐 문제를 만들고, 이를 해결하기 위해 새로운 훅이 등장했다는 진화 과정을 파악하신 것은 좋은 인사이트입니다. 컴퓨터 공학은 언제나 필요에 의해 진화를 해왔기에 그 배경과 서사를 이해한다는게 개발 공부에 큰 도움이 되죠!
이렇게 서사와 배경을 정확히 알게 되면 메모이제이션에 대한 본인의 철학도 취향이 아닌 확신을 통해 만들어지게 되는 것 같아요. 창준의 경우 무분별한 적용보다는 성능 이슈가 실제로 발생했을 때 신중하게 적용하는 접근을 하는 것에 대한 확신과 근거가 생겼기를 바랍니다.
이번 과제를 통해 React가 제공하는 편리한 API들 뒤에 숨어있는 복잡한 문제들을 직접 체험하셨을 거예요. 특히 "메모이제이션은 만능이 아니다"라는 깨달음과 함께 구조적 개선의 중요성을 인식하신 점이 훌륭합니다.
Q1. 업무를 진행하실 때 메모이제이션을 다 해놓은 편인가요? 아니면 성능 이슈가 발생했을 때 적용하시나요?
=> 개인적인 취향 내지는 철학에 관련된 질문이라고 이해하고 개인적인 답변을 하자면 저는 useMemo식의 메모제이션을 가급적 잘 쓰지 않으려고 합니다. 리렌더링의 이유가 달라지는 컴포넌트를 쪼개고 컴포넌트에 props를 통한 memo를 거는 방법을 선호합니다.
Q2. 만약 성능이슈가 발생해서 메모이제이션을 적용하신 경험이 있다면 한번 소개해주시면 감사합니다!
=> 저는 어플리케이션류는 React를 주로 쓰지 않았고 제가 해봤던 React의 경우는 주로 대시보드 형 페이지라 성능이슈가 발생해본 경험이 없네요.
Q3. 컨텍스트와 상태관리에 대한 코치님의 생각도 궁금합니다.
=> Context는 값이 변경이 되면 전체를 리렌더링을 발생시키기에 상태관리로는 부적합합니다. 자주 변하지 않을 값 그렇지만 변하면 다시 전체를 그려내야하는 값을 보관하거나 값을 격리하는데 유리하죠. 테마라던가 언어, 혹은 각종설정값이나 전역객체들이요.
=> 상태관리는 말 그대로 뷰와 상태를 분리하고자는 개념에서 나온것이기에 Context로 상태관리를 구현할 수 있는 있지만 별개의 개념으로 다루는 것이 맞다고 생각을 합니다. 그리고 상태관리의 store를 격리하기 위해서는 context의 도움이 필요하겠죠.
수고하셨습니다. 깊이를 탐구하는 지난 몇 주간의 Deep dive 경험이 즐거운 시간과 함께 탐구하는 방법에 대한 시야를 넓혀주는 계기가 되었기를 바래요. 앞으로 하게 될 클린코드 챕터에서도 이런 탐구 정신을 마음껏 발휘해보시기 바랍니다. 화이팅입니다!