[5팀 여찬규] Chapter 1-3. React, Beyond the Basics#37
Open
chan9yu wants to merge 31 commits intohanghae-plus:mainfrom
Open
[5팀 여찬규] Chapter 1-3. React, Beyond the Basics#37chan9yu wants to merge 31 commits intohanghae-plus:mainfrom
chan9yu wants to merge 31 commits intohanghae-plus:mainfrom
Conversation
- spa 우회하기 위한 404.html 추가 - deploy workflow 추가
chore: 배포를 위한 설정 추가
Feat/shallow equals
Feat/deep equals
Feat/hooks
refactor: dispatchWithCondition 함수를 활용한 deepEquals, shallowEquals 리팩토링
Feat/hoc
Feat/hooks
Feat/context
|
부수자 |
Member
Author
박살내버려 |
|
멘토링 받은 내용 바로 적용하는 모습이 인상적이네요 |
|
글 잘읽었습니다. 저도 지훈님과 생각이 같아요 멘토링 받은내용을 회고에 녹이는게 인상적이였습니다. |
YeongseoYoon-hanghae
approved these changes
Jul 26, 2025
YeongseoYoon-hanghae
left a comment
There was a problem hiding this comment.
바쁜 와중에도 최선을 다하신 점 너무 좋습니다 4주차도 킵고잉~
JiHoon-0330
reviewed
Jul 26, 2025
| const showWithHide: ShowToast = (...args) => { | ||
| show(...args); | ||
| const showWithHide = useAutoCallback((message: string, type: ToastType) => { | ||
| show(message, type); |
There was a problem hiding this comment.
message, type 을 입력받도록 변경한 것이 참신한 것 같아요
JiHoon-0330
reviewed
Jul 26, 2025
| */ | ||
| export const deepEquals = (a: unknown, b: unknown) => { | ||
| return a === b; | ||
| return dispatchWithCondition<[typeof a, typeof b], boolean>( |
There was a problem hiding this comment.
지난 시간에 배운 방법을 적용하려 하는 모습이 좋은 것 같습니다
JiHoon-0330
reviewed
Jul 26, 2025
|
|
||
| const shouldRender = !equals(cache.current.prevProps, props); | ||
| if (shouldRender) { | ||
| cache.current.lastResult = Component(props); |
There was a problem hiding this comment.
리액트 컴포넌트를 함수로 호출했을 때 예상하지 못한 동작을 유발할 수 있어서 React.createElement 를 사용하는 걸 추천 드립니다!
JiHoon-0330
reviewed
Jul 26, 2025
| // 매 렌더링마다 최신 함수로 업데이트 | ||
| fnRef.current = fn; | ||
|
|
||
| const autoCallback = useCallback((...args: Parameters<T>) => { |
JiHoon-0330
reviewed
Jul 26, 2025
|
|
||
| const setShallow = useCallback((newValue: SetStateAction<T>) => { | ||
| setValue((prev) => { | ||
| const nextValue = typeof newValue === "function" ? (newValue as (prevValue: T) => T)(prev) : newValue; |
There was a problem hiding this comment.
as 키워드를 사용하지 않는 방법도 고려해보면 좋을 것 같아요
yuhyeon99
reviewed
Jul 26, 2025
|
|
||
| const hideAfter = debounce(hide, DEFAULT_DELAY); | ||
| const { show, hide } = useMemo(() => createActions(dispatch), [dispatch]); | ||
| const hideAfter = useMemo(() => debounce(hide, DEFAULT_DELAY), [hide]); |
Yangs1s
reviewed
Jul 26, 2025
| */ | ||
| export const shallowEquals = (a: unknown, b: unknown) => { | ||
| return a === b; | ||
| return dispatchWithCondition<[typeof a, typeof b], boolean>( |
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://chan9yu.github.io/front_6th_chapter1-3/
기본과제
equalities
hooks
High Order Components
심화 과제
hooks
context
과제 셀프회고
벌써 리액트 딥다이브 마지막 주차를 마무리하게 되었네요
1주차와 2주차에서 많은 것들을 배워둔 덕에 3주차 과제를 정말 재미있게 진행할 수 있었던 거 같습니다!
이번 주차를 통해 리액트의 핵심 개념들을 깊이 이해할 수 있었어요.
특히 리액트가 왜 얕은 비교를 사용하는지, 불변성을 권장하는 이유가 무엇인지,
그리고 hook들이 실제로 어떤 방식으로 구현되어 있는지를 직접 체험해볼 수 있어서 정말 의미있는 시간이였습니다
이번 주도 5팀은 각자 과제에 대해 구현하고 정리한 내용을 공유했었는데요,
아쉽게도 저는외근과 야근 이슈가 겹치면서 하나밖에 정리하지 못했네요 🥲
그래도 점심시간이나 자투리 시간을 최대한 활용해서 틈틈이 과제를 진행했고,
다행이 모든 과제를 완료할 수 있었습니다. 정말 힘들었던 한 주 였지만, 무사히 마무리해서 뿌듯?합니다 ㅎㅎ
이렇게 바쁜 상황에서도 과제를 이어나갈 수 있었던 것은 팀원분들의 도움과 응원? 그리고 자극 덕분이였던 것 같습니다.
(과제에 대해 항상 적극적이고 서로 정보를 공유해 주는 팀원분들 보기 좋아요)
현재까지 정리한 내용들:
기술적 성장
실제 리액트 내부에서 구현된 훅들을 직접 구현해보면서 동작 원리를 깊이 이해할 수 있어서 정말 좋았습니다!
예를 들어 useRef는 생각보다 단순하게 구현할 수 있었는데요
useState를 이용해서 참조를 유지하는 객체를 생성하는 방식으로도 구현할 수 있다는 것을 배웠습니다.
하지만 나중에 위 코드에서 문제를 발견했는데요,
useState를 사용하면 initialValue가 변경될 때 마다 새로운 객체가 생성될 수 있는데 이를 방지하기 위해
lazy initialization을 사용해서 초기값을 한 번만 설정하고 이후 변경되지 않게 개선했습니다.
그리고 useMemo와 useCallback도 구현하면서 내부 로직에 대해 파악할 수 있었습니다
이렇게 내부적으로 얕은 비교를 통한 메모이제이션이 다양한 곳에서 활용되고 있구나 하고 깨달았습니다.
자랑하고 싶은 코드
shallowEquals함수 구현부입니다.항상 하던 것처럼 하나의 함수에 로직을 때려 넣어서 구현했는데,
"하나의 함수에 너무 많은 일을 하고 있지 않을까? 또 가독성이 안 좋아 보이는데 다른 사람들이 읽을 때 이해할 수 있을까?"라는 고민이었습니다.
이 고민은 2주 차까지 했었던 것 같아요 그래서 2주차 과제 제출할 때도 클린 코드 측면에서도 리뷰를 부탁드렸던 거 같습니다
다행히도 이번 과제 발제 때 준일 코치님이 제 고민을 시원하게 긁어주셨는데, 바로 선언적으로 코드를 리팩토링 해주시고 설명해주셨습니다
그때를 참고해서
shallowEquals를 선언적 구조로 리팩토링을 진행해 봤어요.커밋 기록
AS-IS 하나의 함수 내부에 여러 로직이 결합됨
TO-BE 헬퍼 함수를 통해 선언적으로 리팩토링
가독성과 간결함이 높아졌고, 함수의 결합도가 느슨해짐
이렇게 리팩토링 함으로서 가독성이 올라가고 함수를 이해하는데 더 쉬워졌습니다
하지만 여기서 더 리팩토링을 해봤는데요
준일코치님의 코드를 참고해서 여기서 한 단계 더 리팩토링을 진행했습니다.
최종 리팩토링 결과물
굉장히 직관적으로 코드가 리팩토링된 거 같아서 개인적으로 제일 마음에들고 자랑하고 싶은 코드입니다!
개선이 필요하다고 생각하는 코드
useShallowSelector조금 아쉬운 부분이 있습니다매번 새로운 함수를 반환하고 있어서, 이 자체로 인한 리렌더링이 발생할 수 있을 것 같다?라는 생각이 듭니다
useCallback으로 한 번 더 감싸주면 좋을 것 같은데, 순환 참조 문제 때문에 고민이 되고있습니다
시간상 재대로 보진 못했지만 개선이 될 수 있지 않을까 생각이 되네요
학습 효과 분석
이번 과제에서 가장 큰 배움이 있었던 부분은 리액트의 내부 동작 원리를 직접 구현해보면서 이해할 수 있었던 점입니다.
특히
useSyncExternalStore를 사용해서 외부 스토어와 리액트를 연결하는 방식이 정말 재미있었습니다.useSyncExternalStore를 사용하는 훅중에 useStorage 부분이 있는데,
이렇게 간단하게 localStorage 변화를 리액트가 감지할 수 있게 만들 수 있다는 게 신기했습니다.
그리고 observer 패턴에 대해서도 조금 더 익숙해진 거 같습니다.
createObserver를 통해 Publisher-Subscriber 패턴을 직접 구현해보니, 상태 관리 라이브러리들이 어떤 방식으로 동작하는지 조금은 알 거 같습니다
과제 피드백
3주차 밖에 안되었지만 이번주차가 개인적으로 제일 재미?있었던 주차였던 거 같아요
시간문제로 대충 훑고 넘긴 개념들도 많아서 나중에 시간내서 좀 더 보충하면서 공부해보겠습니다
좋은 과제 감사합니다
학습 갈무리
리액트의 렌더링이 어떻게 이루어지는지 정리해주세요.
메모이제이션에 대한 나의 생각을 적어주세요.
개인적으로 메모이제이션은 양날의 검 이라고 생각을 합니다
일단 메모이제이션은 언제 필요할까? 라고했을 때
정도로 생각해볼 수 있을 거 같아요
이번 과제에서 useMemo를 구현해보니까 메모이제이션도 결국 이전 값을 저장하고 비교하는 오버헤드?가 있다는 것을 알 수 있었는데요
이 비교 과정 자체도 비용이 든다고 생각합니다.
그래서 모든 곳에 메모이제이션을 적용하면 성능이 정말 좋아질까..? 잘못 사용하면 어쩌면 더 비용이 많이들지 않을까?
라고 생각을 했었고 잘못사용한다면 오히려 악효과가 난다는 결론을 지었습니다.
결론적으로, 메모이제이션은 정말 필요한 곳에만 사용해야 한다고 생각해요.
성능 측정을 통해 실제로 병목이 되는 부분을 찾아서 적용하는 게 맞는 것 같습니다.
컨텍스트와 상태관리에 대한 나의 생각을 적어주세요.
컨텍스트가 필요한 이유
라고 생각합니다
하지만 컨텍스트도 만능이 아니다라는 걸 깨달았는데,
이번 과제에서 리팩토링을 했던 ToastProvider를 보면
Command와 State를 분리해서 불필요한 리렌더링 방지하고 useMemo로 최적화 및 타입 안전성을 보장하지만,
만약 Context가 너무 많아지면 Provider 지옥이 될 수 있을 거 같고 전역 상태가 너무 커지면 관리가 어려워질 수 있을 거 같다라는 생각이 듭니다.
이런 부분들 때문에 외부 상태관리 라이브러리를 사용하는게 아닌가 싶어요
간단한 상태관리는 컨텍스트로도 충분하지만, 복잡한 상태 로직이나 여러 컴포넌트에서 독립적으로 사용해야 하는 상태들은 외부 라이브러리가 더 적합할 수 있다고 생각하게 되었습니다.
리뷰 받고 싶은 내용
1. shallowEquals
현재
dispatchWithCondition함수를 활용해서 선언적으로 리팩토링한shallowEquals코드가 실제 성능 면에서도 이점이 있을지 궁금합니다.가독성은 좋아졌지만, 매번 배열을 생성하고 함수 호출 스택이 깊어지는 것이 성능상 문제가 될 수 있을까요?
특히 메모이제이션에서 자주 호출되는 함수인데, 이런 추상화가 적절한 지 궁금합니다
2. useShallowSelector
useShallowSelector에서 useRef로 이전 결과를 저장하고 있는데
큰 객체를 선택하는 selector의 경우, prevResult.current에 계속 참조가 남아있어서 가비지 컬렉션?이 되지 않을 수 있을 것 같습니다.
컴포넌트가 언마운트되거나 selector가 변경될 때 이전 결과를 정리해주는 로직이 필요할까요?
3. ToastProvider
현재 ToastProvider에서 Command와 State Context를 분리했는데
이런 방식이 실제 프로젝트에서도 유지보수하기 좋은 패턴일까요?
아니면 하나의 Context로 합치되 useMemo로 최적화하는 것이 더 나을까요?
Context 분리에 대해 코치님의 의견이 궁금합니다