효뚜르팝의 Dev log
[항해플러스] 3주차 회고 - React, Beyond the Basics 본문
React, Beyond the Basics
feat) React 성능 최적화 & Context 리팩토링
✅ Keep (계속 유지하고 싶은 나의 방향)
- React.memo, useMemo, useCallback 등 리렌더링 최적화를 위한 도구들을 실제로 의도적으로 적용해본 경험.
- 테스트 기반으로 성능 이슈를 직접 확인하고 구조를 리팩토링한 흐름. 테스트 없었으면 놓쳤을 문제들도 있었을 것 같다.
- 유지보수와 확장성을 고려하여 기능 단위로 폴더를 나누고, 각 도메인마다 Context/Provider/hooks/UI를 분리한 구조로 설계했다.
- 특히 useItems, useComplexForm 등 커스텀 훅으로 UI와 로직을 분리한 점이 코드 가독성이나 응집도 면에서 효과적이었던 것 같다.
- 이번 과제를 하며 "내가 왜 이 훅을 써야 하지?"에 대한 기술적 의문을 처음으로 스스로 던지고 고민했다는 점이 가장 의미 있었다.
❗ Problem (고민되거나 부족했던 부분)
- useCallback과 useMemo를 자주 써왔지만, 그 내부 구조가 동일하다는 걸 이번에 처음 제대로 인지했다.
자주 쓰던 도구도 이해 없이 써왔다는 사실이 조금 부끄럽기도 했다. - 초반엔 useCallback 쓰면 무조건 최적화된다고 생각했지만,
실제로는 의존성 배열이 유지될 때만 동일한 참조가 유지되고 최적화 효과가 발생한다는 걸 이번에야 알게 됐다. - NotificationContext 내부의 상태가 변하면 AuthProvider까지 함께 리렌더링되는 구조였고,
그 결과 의도치 않게 ItemList, ComplexForm까지 리렌더링되는 테스트 실패가 발생했다. - 다음은 실패했던 테스트 예시다:
// 테스트 목표: 로그인/로그아웃 시 Header, ComplexForm, NotificationSystem만 리렌더링되어야 함
expect(renderLogMock).toHaveBeenCalledWith('Header');
expect(renderLogMock).toHaveBeenCalledWith('ComplexForm');
expect(renderLogMock).toHaveBeenCalledWith('NotificationSystem');
- 그러나 실제로는 ItemList까지 리렌더링되거나, 반대로 ComplexForm이 빠져서 테스트가 실패했다.
- AuthProvider 내부에서 useNotificationContext()를 직접 호출하고 있었음
- 그 의존성을 제거하기 위해 NotificationProvider 구조를 바깥으로 감쌌고
- AuthProvider는 오직 로그인 관련 기능만 담당하도록 관심사를 분리하는 식으로 해결했다.
🚀 Try (앞으로 더 깊게 들어가고 싶은 것들)
- 커스텀 훅 안에서 useCallback으로 감쌀지, 컴포넌트 바깥에서 제어할지에 대한 명확한 기준을 세우고 싶다.
지금은 약간 감으로 쓰는 느낌이라 실무에서는 구조적으로 불안할 수 있다고 생각했다. - 중첩된 Provider 구조에서 각 Provider의 책임을 더 분리해서 명확하게 설계하는 방법을 연습해보고 싶다.
- Context 내부의 value 또한 useMemo, useCallback으로 철저하게 고정하여 참조 변경을 최소화했다:
const addNotification = useCallback((msg, type) =>
{ const id = Date.now(); setNotifications((prev) => [...prev, { id, message: msg, type }]); }, []);
const value = useMemo(() => ({ notifications, addNotification, removeNotification }),
[notifications, addNotification, removeNotification]);
- 앞으로는 렌더링 성능을 정량적으로 추적할 수 있는 도구들 (React Profiler, why-did-you-render)도 직접 써보며 확인해보고 싶다.
- 그리고 기술적인 구조뿐 아니라 UX 흐름(예: 로그인 후 알림이 뜨는 타이밍 등)까지 고려하는 시야를 넓히고 싶다.
이번엔 기능 구현에 집중하다 보니 사용자 경험을 세심하게 설계하진 못했던 것 같다.
💡 새롭게 얻은 인사이트
이번 구조 리팩토링을 하며 가장 크게 얻은 인사이트 중 하나는,
Context를 반드시 전역(Global)으로 관리할 필요는 없다는 것이었다.
기능적으로 명확히 구분되는 도메인이라면,
굳이 Context로 끌어올리지 않고 커스텀 훅이나 컴포넌트 내부 상태로 관리하는 방식이 더 자연스럽고 응집력 있는 구조가 될 수 있다는 걸 직접 경험했다.
이전에는 'Context = 전역 상태'라는 생각에 모든 상태를 Context로 끌어올렸는데,
이번 과제를 통해 오히려 상황에 따라 로컬 상태를 유지하는 것이 더 낫다는 판단 기준을 세울 수 있었다.
덕분에 Context의 쓰임과 위치, 그리고 경계를 다시 생각하게 되었고,
"Context는 작게 쪼개고, 관심사는 명확히 분리한다"는 구조적 관점을 처음으로 스스로 정립하게 된 것 같다.
🧘🏻♀️ 나의 회고 한 줄
자주 쓰던 훅들의 동작 원리를 ‘이제서야’ 이해하게 됐고,
무심코 써왔던 기술들이 실제로 어떻게 작동하고, 어디에 영향을 미치는지를 고민하기 시작했다는 것,
그 자체가 내게 가장 큰 성장이었다.
'회고록' 카테고리의 다른 글
| 2025 회고 | 번아웃과 퇴사 이후, 3년차 개발자가 다시 삶을 선택하기까지 (0) | 2026.01.04 |
|---|---|
| [항해플러스] 5주차 회고 - 디자인 패턴과 함수형 프로그래밍 (0) | 2025.04.28 |
| [항해플러스] 2주차 회고 - 프레임워크 없이 SPA 만들기 (0) | 2025.04.06 |
| [항해 플러스] 1주차 회고 - 바닐라 JS로 SPA 구조 직접 구현해보기 (0) | 2025.03.28 |
| 2024 회고 (3) | 2025.01.05 |