Hook의 동작 원리에 대해 분석해보려고 합니다.
위에서 볼 수 있듯이 아래의 순서로 구현채를 확인 할 수 있습니다.
import { useState, useEffect, ... } from './ReactHooks'
import ReactCurrentDispatcher from './ReactCurrentDispatcher'
const ReactCurrentDispatcher = {
current: null,
}
export default ReactCurrentDispatcher
core는 단순히 React element만 알고 있습니다. 실제 Hook을 포함한 elemenet 객체는 모릅니다. 그런데 React element를 실제 살아숨쉬는 fiber로 확장하는 역할은 core가 아닌 reconciler가 합니다.
그렇지만 위에서 분석한것은 코드에서 봤던 core만 hook을 가져오고 reconciler에 훅을 가져오는 부분은 없습니다.
이는 reconciler에서 hook을 다룰때 가져오는 방식이 아닌 주입하는 방식으로 다루기 때문입니다. 이렇게 외부에서 주입하는 방식은 코어가 직접 하지는 않고 중간자가 이 역할을 합니다. 그 중간자는 shared 패키지, ReactSharedInternals.js가 합니다.
spring의 DI(Dependency Injection) 처럼
ReactSharedInternals.js와 같은 중간자를 사용하는 것은 React의 내부 동작을 추상화하고 공유 리소스에 접근하는 표준화된 방법을 제공합니다. 이것은 React의 핵심 동작을 분리하고 플랫폼, 환경 또는 확장에 대한 대응을 단순화하며, React를 보다 견고하고 확장 가능하게 만듭니다. 이러한 설계 결정은 React를 더 강력하고 다양한 상황에서 활용할 수 있게 만들어줍니다.
외부 주입 역할을 하는(의존성 관리) ReactSharedInternals.js와 shared 패키지
ReactCurrentDispatcher.js 모듈에서 아래 코드는 동적 바인딩을 위한 패턴으로 보이는데요. 동적 바인딩이 무엇인지에 대해 자세히 알아보도록 합시다.
import ReactCurrentDispatcher from './ReactCurrentDispatcher'
function resolveDispatcher() {
const dispatcher = ReactCurrentDispatcher.current
return dispatcher
}
export function useState(initialState) {
const dispatcher = resolveDispatcher()
return dispatcher.useState(initialState)
}
export function useEffect(create, inputs) {
const dispatcher = resolveDispatcher()
return dispatcher.useEffect(create, inputs)
}
동적 바인딩(Dynamic Binding)은 프로그램에서 변수, 함수 또는 객체와 관련된 연산이 실행될 때 런타임(실행 시간)에 연결되는 것을 의미합니다. 이것은 정적 바인딩(Static Binding)과 대조됩니다. 정적 바인딩은 컴파일 시간에 변수나 함수가 어떻게 연결되는지 결정되고, 런타임에 변경되지 않는 것을 의미합니다.
이를 통해 얻을 수 있는 것들은 아래와 같습니다.
- 객체 지향 프로그래밍에서 다형성을 구현하는데 사용됨, 코드의 유연성을 높일 수 있음
- 이벤트 처리
- 플러그인 아키텍쳐에서 유용하게 사용
- 리플렉션
resolveDispatcher 함수는 동적 바인딩을 사용하여 현재의 디스패처(dispatcher) 객체를 결정합니다. 이를 통해 환경에 맞는 dispatcher를 가져옵니다.
useState와 useEffect 함수는 상태 관리와 효과 처리를 위한 React의 핵심 기능입니다. 이 함수들은 실제 구현은 dispatcher 객체에 위임하고, resolveDispatcher 함수를 통해 현재 환경에 맞는 디스패처를 가져와서 해당 디스패처의 useState 및 useEffect 메서드를 호출합니다. 이렇게 하면 React는 현재 환경에 맞는 구현을 사용하게 되며, 다른 환경에서도 동일한 코드를 사용할 수 있습니다.