-
Notifications
You must be signed in to change notification settings - Fork 62
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
[9팀 박지수] [Chapter 1-3] React, Beyond the Basics #7
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
코드 너무너무 잘 봤습니다!
지수님 코드를 보니 모르는 부분이 많아서 코드 리뷰를 하면서 잘 배워간다는 느낌을 많이 받았습니다!
이번 주차도, 이후 주차도 화이팅입니다!
@@ -27,6 +27,12 @@ export default tseslint.config( | |||
], | |||
}, | |||
}, | |||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
eslint의 룰을 직접 작성해보셨군요!
좋은 인사이트 얻어갑니다!
src/@components/Portal.tsx
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
포탈로 구현하셨네요!
저는 여기서 포탈로 구현하신 분 지수님이 처음인 것 같아요!
멋지십니다!
참고로 말씀드리면 나중에 index.html을 수정하시지 않고 직접 추가하는 방식도 있답니다...! (이미 알고계신 것 같긴합니다 ㅎㅎ)
interface ContextState { | ||
group: Map<number, NotificationType>; | ||
} | ||
|
||
interface ContextActions { | ||
addNotification(notification: NotificationType): void; | ||
removeNotification(id: number): void; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
State & Action으로 나누시다니 코드 정말 잘짜시네요!
또 한 번 배우고 갑니다!
export const [ | ||
NotificationSystemStateProvider, | ||
useNotificationSystemStateContext, | ||
] = createSafeContext<ContextState>("NotificationSystemProvider"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
createSafeContext는 처음보네요! createContext와는 어떤 차이점이 있나요!?
import { isPlainObject, isPrimitive } from "../utils"; | ||
|
||
export function deepEquals<T>(first: T, second: T): boolean { | ||
if (isPrimitive(first) || isPrimitive(second)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
비교 함수를 따로 빼셨네요! 저는 과제 통과를 우선으로 생각해서 구현하지 않았는데 한 수 배웠습니다!
export function useMemo<T>( | ||
factory: () => T, | ||
_deps: DependencyList, | ||
_equals = shallowEquals, | ||
): T { | ||
// 직접 작성한 useRef를 통해서 만들어보세요. | ||
return factory(); | ||
const memoized = useRef<{ value: T; deps: DependencyList }>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
하나의 ref로 작성하셨군요..! 저희 7,10팀원분들도 전부 하나로 작성하셨더라구요.. 저만 두개로 작성했나봐요! 하나로 구현하는 것도 연습해봐야겠습니다!
// React의 useState를 이용해서 만들어보세요. | ||
return { current: initialValue }; | ||
return useState(() => ({ current: initialValue }))[0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
변수를 만들지 않는 깔끔함..!
좋은 인사이트 감사드려요!
아 그리고 위의 useRef를 여러 번 선언하신 이유가 있나요? react의 코드에서는 여러 개의 타입을 받기 위해서 선언한 것은 보았는데 여기에서도 구현하신 이유가 궁금합니다!
@@ -0,0 +1,31 @@ | |||
import { createContext, useContext, useMemo } from "react"; | |||
|
|||
export function createSafeContext<ContextValue extends object | null>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
위에서의 궁금하던 createSafeContext가 여기있었네요!
저는 다른 함수가 있는 줄 알았는데 직접 구현하신 것이었군요!
대단하십니다..!
Provider까지 내부에서 구현하신 것을 보니 굉장히 깔끔하게 코드가 짜여져 있다고 생각이 듭니다!
다음 번에는 내부의 Provider와 useSafeContext도 분리해서 구성해보는 것도 괜찮다고 생각됩니다!
좋은 인사이트 감사드려요!
@@ -0,0 +1,5 @@ | |||
export function isNil<T>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nil 이라면... 다른 언어를 배워보셨군요...!
어떤 걸 배우셨나요?
과제 체크포인트
기본과제
심화 과제
과제 셀프회고
이번 주 과제는 지난 1, 2주차에 비해 비교적 수월하게 진행할 수 있었다. 본인 같은 경우 심화 과제보다는 기본 과제를 진행하며 더 많은 고민과 생각을 하게 되었는데, 이는 기본기가 부족한 탓일지도 모르겠다. 하지만 그동안 제공되는 기능을 문서만 보고 별 생각 없이 사용했던 것과 달리, 내부 원리를 어렴풋이나마 이해하게 되면서 재미를 느낄 수 있었다.
기술적 성장
처음
useRef
를 직접 구현할 때, 내부에useEffect
를 사용하면서 "타이밍 이슈가 발생하지 않을까?"라는 고민에 학습 메이트(최기환 님)의 조언을 구하게 됐는데 그냥 아무것도 사용하지 않고useRef
만으로 구현하는게 좋겠다는 조언을 얻을 수 있었다. 결과, 고민했던 부분을 깔끔하게 해결할 수 있었는 이를 통해 리액트 생명주기에 대한 이해가 아직 부족하다는 것을 깨닫는 좋은 기회가 되었다.코드 품질
이번 과제는 "메모이제이션", "리렌더링 최소화"에 초점이 맞춰져 있는 것 같아 그 부분에 신경을 써서 과제를 진행했다. 다만 언젠가 메모이제이션에 대해 상반되는 두 가지 의견을 접한 적이 있었는데 다음과 같았다.
두 의견 모두 일리가 있는 것 같았지만, 과제를 진행하는 동안에는 구분해서 사용하는 것이 본인의 성장에 도움이 될 것이라고 판단하여 다음과 같은 기준을 적용했다.
useMemo
: 값의 업데이트가 빈번하지 않을 것으로 예상되는 경우에 사용useCallback
: 예전에 접했던 토스의usePreservedCallback
과 Radix UI의useCallbackRef
라는 커스텀 훅을 떠올리며usePreservedCallback
이라는 커스텀 훅을 만들어 사용했다. 이 훅은 함수의 참조 값은 유지하면서 의존성 배열 없이도 함수 내부에서 사용하는 상태 값이 업데이트될 때 그 값을 반영할 수 있도록 한다. 주로onClick
이나onChange
등의 이벤트 함수에 사용했다. 다만usePreservedCallback
은 참조 값이 변하지 않기 때문에 함수의 업데이트를 감지해야 하는 상황에서는useCallback
을 사용해야 되겠다.memo
: 사용하지 않아도 과제 통과에는 문제가 없었다. 그래도Portal
컴포넌트에 달아주어 외부와 격리시키는 느낌으로 진행해 봤다.그래서 기준을 바탕으로 다음 코드와 같이 값이 수시로 바뀌는 경우에는 메모이제이션을 하지 않았다.
Context API 사용에 있어서도 여러 가지 개선을 시도했다. 먼저 Radix UI의
createSafeContext()
함수를 참고하여 undefined safe한? 컨텍스트 생성 함수를 구현했다.이를 통해서 안전 컨텍스트의 생성이 가능하도록 했다. 다음으로 컨텍스트를 만들어볼 차례인데 이전에는 상태(state)와 액션 함수(action functions)를 하나의 컨텍스트에서 관리하여 불필요한 리렌더링이 발생하는 문제가 있었다.
위와 같이 하나의 컨텍스트를 사용할 경우, 액션 함수만 사용하는 컴포넌트에서도 상태 값의 업데이트가 발생할 때 리렌더링이 발생했다. 따라서 이번 과제에서는 상태 컨텍스트와 액션 컨텍스트를 분리하여 사용했다.
상태와 액션 컨텍스트를 분리하여 사용한 결과, 상태 값이 변경되더라도 액션 함수만 사용하는 컴포넌트들은 리렌더링되지 않는 것을 확인했다.
학습 효과 분석
과제 피드백
본인만 그렇게 느끼는 것일 지도 모르겠지만 이번 주차 과제는 지난 1, 2주차 과제보다 쉽게 느껴졌다.
좀 더 난이도 올려도 괜찮을 듯?
다만 과제 진행 중
ComplexForm
컴포넌트에서 약간의 문제가 발생했었는데, 본인 생각에 "요번 심화 과제는 리렌더링에 초점이 맞춰져 있고,ComplexForm
컴포넌트는 user의 값과 notifications 값을 내부에서 사용하지 않으니 그럼 두 값이 변경되더라도 리렌더링을 하면 안되겠네?!"라고 안일하게 판단하며 아래의 AS-IS 코드와 같이 코드를 작성 했더니 테스트를 통과하지 못했다.이유를 찾고자 테스트 코드를 까보니 user 값과 notification 값의 변경이 있을 때도
ComplexForm
컴포넌트는 리렌더링 되어야 한다더라... 처음엔 그 사실도 모르고 엄청 헤맸는데 이에 대한 해결 방안으로, 아래와 같이 코드를 추가하여 테스트를 통과할 수 있었다.리뷰 받고 싶은 내용
파일명.types.ts
라는 파일에 관리하는 곳도 있고 단일 파일로 소스 코드와 함께 작성하는 곳도 있던데 기준이 궁금. 개인 취향인가?작성 중···