프레임워크 만들며 고민 이모저모

gun·2024년 2월 3일
0
post-thumbnail

머리 식히기??????

프레임워크 만들기 프로젝트는 처음에는 단지 머리 식히기용으로 시작했지만, 예상치 못한 여정으로 나를 이끌었다. 공부하고 만들면서 어려움에 부딪히고, 끊임없이 개선 아이디어를 고민하며 수 많은 시간을 보냈다. 처음에는 글을 쓰려고 한 것도 아니었지만, 동기부여를 위해 블로그에 글을 쓰게 되었다.

도전 과제들이 많아서 고민이 되지만, 그것이 즐거움(?)의 시작이기도 하다. 사이즈가 큰 과제들이 5개나 되는데, 더 늘어날 가능성까지 염두에 두고 있다. 어떤 것을 먼저 해야할지 고민하면서도 이 모든 것이 나를 설레게(?) 만들고 있디.... 나의 ~갈팡질팡 코딩~ 을 감당 할 수 있게 최대한 변경이 쉽고 확장 가능한 코드를 작성 해야겠다..

대충 메모장에 적어둔 다음 과제 내용

  • 함수로 리팩터링
  • typescript로 리팩터링
  • zustand와 같은 상태 관리 라이브러리 추가
  • babel을 사용 해 jsx문법을 사용 할 수 있도록 수정 (일부러 안하고 있음) 사용하면 dom을 그려주는게 더 편할듯..
  • rendering 최적화 = 지금은 rerender가 발생하면 화면을 다시 그려주는 방식을 채택 하고 있음. 브라우저에 리페인팅, 리플로우가 굉장이 많이 발생 하고 있음. = 브라우저가 너무 많은 일을 하고 있으므로 이건 jsx를 추가하며 수정 할 수 있지 않을까? = jsx를 도입하면서 React의 virtual dom을 같이 써보자
  • ... 추가 중..

또 고민

React 상태 변화에 따른 렌더링 관련하여 오픈소스를 살펴보면서 느낀 점은, 상태를 immutable하게 관리해야겠다는 것이었다. 그러나 mutable한 상태 관리 방법에도 공유할 만한 proxyObject.defineProperty() 알찬 메서드가 있는데, 이걸 설명 하지 포스팅 하지 않고 넘어가기에는 너무너무 아쉽다. 다음 포스팅에서는 두 가지 상태 관리 방법에 대해 설명하고자 한다.

블로그를 쓰는 것은 시간이 많이 소요되지만, 아래는 내가 고민하며 무작정 적어둔 글이니, 그저 내 생각을 확인하는 정도로만 읽어주시면 좋겠다.

제목 - 두서 없는 고민..

  • 프레임워크들은 상태를 immutable || mutalble 하게 관리하고 있다.

  • 현 프로젝트에서 mutable한 상태를 관리 하게 된 이유는 불필요한 메모리를 소비 하지 않기 위해 mutable한 상태를 관리하고, 좀더 직관적인 상태 변화를 보고 싶어서 였으나,

  • 개발 하면서 느낀점은 state 하나가 변하면 모든 화면은 다시 그려지고 있음.

  • jsx를 사용한들 이 문제점은 해결 되지 않음

  • 다시 immutable한 상태를 관리 하기 위해 React의 내부 구현을 참고 함

useState 사용 시

// https://github.com/facebook/react/blob/d29f7d973da616a02d6240ea10306a6f33e35ca1/packages/react/src/ReactHooks.js#L23
export function useState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  const dispatcher = resolveDispatcher();
  return dispatcher.useState(initialState);
}
dispatcher.useState
// https://github.com/facebook/react/blob/d29f7d973da616a02d6240ea10306a6f33e35ca1/packages/react-reconciler/src/ReactFiberHooks.js#L1767-L1779
function mountState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {

  * const hook = mountStateImpl(initialState);  // React fiber 의 scheduling 관련 로직들이 담겨 있음
  const queue = hook.queue;
  const dispatch: Dispatch<BasicStateAction<S>> = (dispatchSetState.bind(  // 외부로 노출되는 함수로 bind로 각 함수들이 묶여 있음
    null,
    currentlyRenderingFiber,
    queue,
  ): any);
  queue.dispatch = dispatch;
  return [hook.memoizedState, dispatch];   // 우리가 만나게 되는 const [state, setState] = useState
}
  • hook을 따라가게 되면 만나는 함수

참고 해야 할 부분

  • workInProgressHook는 전역 변수로 스케쥴링을 담당하고 있음.
  • linked list 형태로 현 queue가 null 이라면 currentlyRenderingFiber.memoizedState = workInProgressHook = hook; workInProgressHook를 header로 잡고
  • workInProgressHook = workInProgressHook.next = hook next로 이어서 사용한다.
  • 화면을 그려주는 부분까지 보진 못했지만, linked list로 상태를 순차적으로 업데이트 하고 화면에는 한번에 반영하는 방법으로 수정 하여야 겠다
// mountStateImpl-  https://github.com/facebook/react/blob/d29f7d973da616a02d6240ea10306a6f33e35ca1/packages/react-reconciler/src/ReactFiberHooks.js#L1749C1-L1765C2
// mountWorkInProgressHook - https://github.com/facebook/react/blob/d29f7d973da616a02d6240ea10306a6f33e35ca1/packages/react-reconciler/src/ReactFiberHooks.js#L927-L946

function mountStateImpl<S>(initialState: (() => S) | S): Hook {
  * const hook = mountWorkInProgressHook();
  if (typeof initialState === 'function') {
    // $FlowFixMe[incompatible-use]: Flow doesn't like mixed types
    initialState = initialState();
  }
  hook.memoizedState = hook.baseState = initialState;
  const queue: UpdateQueue<S, BasicStateAction<S>> = {
    pending: null,
    lanes: NoLanes,
    dispatch: null,
    lastRenderedReducer: basicStateReducer,
    lastRenderedState: (initialState: any),
  };
  hook.queue = queue;
  return hook;
}


function mountWorkInProgressHook(): Hook {
  const hook: Hook = {
    memoizedState: null,

    baseState: null,
    baseQueue: null,
    queue: null,

    next: null,
  };

  if (workInProgressHook === null) {
    // This is the first hook in the list
    currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
  } else {
    // Append to the end of the list
    workInProgressHook = workInProgressHook.next = hook;
  }
  return workInProgressHook;
}

0개의 댓글