-
Notifications
You must be signed in to change notification settings - Fork 42
[3팀 김대현] Chapter 4-2. 코드 관점의 성능 최적화 #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
daehyunk1m
wants to merge
14
commits into
hanghae-plus:main
Choose a base branch
from
daehyunk1m:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Conversation
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
- gh-pages 패키지 추가 - Claude Code 학습 환경 설정 파일 추가 - MCP 설정 및 문서 파일 추가 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Promise.all 내부 await 제거로 진짜 병렬 실행 구현 - createCachedFetcher 클로저 함수로 API 캐싱 구현 - 중복 API 호출 제거 (6회 → 2회) - 모달 재오픈 시 API 재호출 방지 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
- useMemo를 활용한 filteredLectures 메모이제이션 구현 - 의존성 배열 [searchOptions, lectures] 지정으로 불필요한 재계산 방지 - Task 2 완료 및 진행 상황 업데이트 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
- allMajors 계산을 useMemo로 메모이제이션 적용 - visibleLectures 계산을 useMemo로 메모이제이션 적용 - MajorList 컴포넌트 분리 및 React.memo 적용 - 불필요한 리렌더링 방지로 성능 개선 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
- LectureItem 컴포넌트 분리 및 React.memo 비교 함수 적용 - MajorItem 컴포넌트 분리 및 React.memo 적용 - MajorList 컴포넌트를 MajorItem으로 대체 - 페이지네이션 시 새로 추가되는 컴포넌트만 렌더링 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
- TableOutline 컴포넌트 분리하여 useDndContext 격리 - ScheduleTable, DraggableSchedule에 React.memo 적용 - 드래그 시 TableOutline만 리렌더링되도록 최적화 - children prop 활용하여 내부 컴포넌트 리렌더링 방지 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
- ScheduleTables: 핸들러 함수 useCallback으로 메모이제이션 - ScheduleTable: Props 타입 변경으로 함수 참조 직접 전달 - ScheduleDndProvider: handleDragEnd 최적화 및 함수형 업데이트 적용 - ScheduleContext: CQRS 패턴으로 Command/Query 컨텍스트 분리 - SearchDialog, ScheduleDndProvider: useScheduleCommand로 불필요한 리렌더링 방지 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
- package.json: gh-pages 스크립트 추가 - vite.config.ts: GitHub Pages base URL 설정 - 모든 태스크 완료 (6/6, 100%) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
- API 호출 경로에 BASE_URL 환경변수 적용 - vite-env.d.ts 타입 정의 파일 추가 - tsconfig.app.json에 vite-env.d.ts include 추가 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
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.
과제 체크포인트
과제 배포 링크
과제 요구사항
Promise.all이해)과제 셀프회고
기술적 성장
새로 학습한 개념
1. CQRS 패턴을 활용한 Context 최적화
기존에는 하나의 Context에 상태와 업데이트 함수를 함께 제공했는데, 이 방식은 상태가 변경될 때마다 업데이트 함수만 필요한 컴포넌트도 리렌더링되는 문제가 있었습니다. CQRS(Command Query Responsibility Segregation) 패턴을 적용하여 Command Context(상태 업데이트)와 Query Context(상태 조회)를 분리했습니다.
이를 통해
SearchDialog와ScheduleDndProvider는useScheduleCommand만 구독하여 상태 변경 시 리렌더링되지 않고, 실제로 상태를 표시하는ScheduleTables만 리렌더링되도록 최적화했습니다.2. 클로저를 활용한 API 캐싱 패턴
클로저가 외부 변수를 "기억"하는 특성을 활용하여 API 응답을 캐싱하는 고차 함수를 구현했습니다.
3. useDndContext 격리를 통한 드래그 성능 최적화
@dnd-kit의useDndContext는 드래그 중 매 프레임마다 상태가 변경되어 구독하는 모든 컴포넌트가 리렌더링됩니다. 이를 해결하기 위해 Context를 사용하는 부분만 별도 컴포넌트로 분리했습니다.기존 지식의 재발견/심화
useMemo/useCallback 선택적 적용의 중요성
처음에는 "모든 함수에 useCallback, 모든 계산에 useMemo를 적용하면 되겠지"라고 생각했지만, 실제로는 메모이제이션 자체에도 비용(의존성 비교, 메모리 사용)이 발생합니다. 이 과제를 통해 다음 기준으로 선택적 적용의 중요성을 체감했습니다:
React.memo 커스텀 비교 함수
기본 React.memo는 props를 얕은 비교를 통해 다름을 판단하는데, 객체 props의 경우 매번 새 참조가 생성되어 메모이제이션이 무력화될 수 있습니다. 커스텀 비교 함수로 실제로 비교가 필요한 값만 확인하도록 최적화했습니다.
구현 과정에서의 기술적 도전과 해결
문제: 드래그 중 시간표 전체가 리렌더링되어 버벅임 발생
원인 분석: React DevTools Profiler로 확인한 결과,
useDndContext를 사용하는ScheduleTable컴포넌트가 드래그 중 60fps로 리렌더링되고 있었습니다.useDndContext는 드래그 위치, 충돌 감지 등의 정보를 실시간으로 업데이트하기 때문입니다.해결 과정:
ScheduleTable에서useDndContext를 사용하는 부분(드래그 중 테두리 표시)을TableOutline컴포넌트로 분리ScheduleTable과DraggableSchedule에React.memo적용TableOutline만 리렌더링되고, 실제 시간표 데이터는 드롭 시에만 업데이트코드 품질
특히 만족스러운 구현
1. ScheduleContext의 CQRS 패턴
단순히 "리렌더링을 줄인다"는 목표를 넘어서, 관심사 분리라는 설계 원칙을 적용했습니다. 상태를 "읽기"와 "쓰기"로 분리함으로써:
2. createCachedFetcher의 재사용성
제네릭 타입을 활용하여 어떤 API fetcher에도 적용 가능한 범용 캐싱 유틸리티를 구현했습니다. 기존 API 호출 코드를 수정하지 않고도 캐싱을 적용할 수 있어 관심사 분리가 잘 되었습니다.
코드 설계 관련 고민과 결정
메모이제이션 적용 범위 결정
모든 컴포넌트에 React.memo를 적용하는 것은 오히려 성능에 해로울 수 있다고 생각했고 다음 기준으로 선택적 적용을 결정했습니다:
하지만 Q&A 시간때 오히려 "가끔" 메모이제이션을 적용하는 것보다 오히려 "모두" 메모이제이션을 적용하는 것이 좋을 수 있다고 하여 이후엔 모두 메모이제이션하는 방향을 고려해볼 예정입니다.
참조 - https://yceffort.kr/2022/04/memo-for-referential-stability-in-react
학습 효과 분석
가장 큰 배움이 있었던 부분
Context 리렌더링 메커니즘의 이해
이론적으로 "Context 값이 변경되면 구독 컴포넌트가 리렌더링된다"는 것은 알고 있었지만, 실제로 어떤 상황에서 문제가 되는지 체감하지 못했습니다. 이 과제에서:
useDndContext가 매 프레임 변경됨을 확인이 과정을 통해 "왜 Context 최적화가 필요한지"를 직접 경험하고, 해결 패턴을 체득했습니다.
Promise.all의 동작 원리 재확인
Promise.all은 배열 내 모든 Promise가 resolve될 때까지 기다립니다. 하지만 단순히 Promise.all로 감싼다고 병렬 실행이 되는 것은 아닙니다:추가 학습이 필요한 영역
과제 피드백
시간표 애플리케이션이라는 구체적인 맥락이 있어서 "왜 이 최적화가 필요한지" 체감할 수 있었습니다. 드래그 중 버벅임은 사용자 경험에 직접 영향을 주기 때문에 최적화의 필요성을 절실히 느꼈습니다.
리뷰 받고 싶은 내용
Q&A에서 "선택적 메모이제이션보다 전체 적용이 나을 수 있다"는 내용이 인상깊었습니다. 현재는 리렌더링 비용이 큰 컴포넌트만 메모이제이션을 적용했는데 React 19의 React Compiler가 도입되면 수동 메모이제이션이 불필요해질 것으로 예상되는데, 현 시점에서도 전체 적용이 더 좋은 전략일까요?