Skip to content

Latest commit

 

History

History
243 lines (183 loc) · 6.02 KB

6주차_심미진_개인.md

File metadata and controls

243 lines (183 loc) · 6.02 KB

selector() 함수의 내부 동작 분석

1. Jotai와 Recoil에서 selector의 주요 역할

  1. 파생 상태(Derived State) 생성
  • 하나 이상의 atom/상태로부터 새로운 계산된 값을 만듦
  • 기존 상태를 기반으로 복잡한 로직을 통해 새로운 상태를 생성
  1. 상태 추상화 및 변환
// 예시: 사용자 목록에서 성인만 필터링
const adultUsersSelector = atom((get) => {
  const users = get(usersAtom);
  return users.filter((user) => user.age >= 18);
});
  1. 성능 최적화
  • 메모이제이션을 통해 불필요한 재계산을 방지
  • 의존하는 atom의 값이 변경되지 않았다면 이전 계산 결과를 재사용
  1. 비동기 상태 처리
const userDataSelector = atom(async (get) => {
  const userId = get(userIdAtom);
  const response = await fetchUserData(userId);
  return response.data;
});
  1. 복잡한 상태 로직 캡슐화
  • 상태 계산 로직을 중앙집중적으로 관리 가능
  • 컴포넌트에서 직접적인 계산 로직을 분리 가능
  1. 읽기/쓰기 selector
const temperatureSelector = atom(
(get) => {
// 읽기: 섭씨를 화씨로 변환
const celsius = get(celsiusAtom);
return (celsius _ 9/5) + 32;
},
(get, set, fahrenheit) => {
// 쓰기: 화씨를 섭씨로 변환하여 설정
const celsius = (fahrenheit - 32) _ 5/9;
set(celsiusAtom, celsius);
}
);

2. 공통점&차이점

  • 공통점

    • 상태 관리의 복잡성을 추상화
    • 계산된 상태의 효율적인 관리
    • 반응형 상태 업데이트(특정 상태가 변경될 때 자동으로 연관된 다른 상태들이 업데이트되는 메커니즘)
  • 차이점

    • Recoil: 더 복잡한 의존성 추적, React Suspense와 통합되어 비동기 상태를 동기적으로 관리할 수 있도록 함
    • Jotai: 더 간결하고 최소한의 API, 성능에 최적화

결론적으로 selector는 상태 관리에서 "계산된 상태"를 만들고 관리하는 강력한 추상화 메커니즘!

3. Jotai의 selector 내부 구현

Jotai의 selector는 기본적으로 atom 함수의 getter 함수를 통해 구현된다

3-1. 기본 원리

function atom(read, write?) {
  return {
    read: (get) => {
      // 다른 atom의 값을 읽어올 수 있는 get 함수
      // 의존성 추적 및 메모이제이션 로직 포함
    },
    write: (get, set, value) => {
      // 쓰기 작업 구현 (선택적)
    },
  };
}

3-2. 의존성 추적 메커니즘

Jotai는 내부적으로 "dependency tracking" 시스템을 구현하고 있다.

selector가 다른 atom을 읽을 때마다 해당 의존성을 자동으로 추적한다.

의존하는 atom의 값이 변경되면 selector도 자동으로 다시 계산된다.

3-3. 메모이제이션 전략

function createMemoizedSelector(computeFn) {
  let lastDependencies = null;
  let lastResult = null;

  return (get) => {
    // 현재 의존하는 atom들의 값 추적
    const currentDependencies = trackDependencies(get);

    // 의존성이 변경되지 않았다면 캐시된 결과 반환
    if (lastDependencies && areEqual(lastDependencies, currentDependencies)) {
      return lastResult;
    }

    // 새로운 결과 계산
    const newResult = computeFn(get);

    // 상태 업데이트
    lastDependencies = currentDependencies;
    lastResult = newResult;

    return newResult;
  };
}

3-4. 비동기 selector 처리

const asyncSelector = atom(async (get) => {
  // 비동기 작업 처리 메커니즘
  const dependencyValue = get(someAtom);
  const result = await fetchSomeData(dependencyValue);
  return result;
});

더 고려해야하는 점

  • 성능 최적화, 다양한 edge case 처리 그리고 React의 동시성 모드와도 호환되도록 설계해야함

내부 구현의 핵심은 "최소한의 재계산"과 "효율적인 의존성 관리"이다.

4. Recoil의 selector 내부 구현

4-1. 기본 구조

function selector({
    key, // 고유 식별자
    get, // 읽기 함수
    set? // 선택적 쓰기 함수
}) {
    // selector의 핵심 로직
}

4-2. 의존성 추적 메커니즘

function createSelector(config) {
  // 의존성 추적을 위한 내부 로직
  const dependencyMap = new Map();

  function trackDependencies(get) {
    // 현재 selector가 읽는 atom들을 추적
    const dependencies = [];

    // 프록시를 통한 의존성 추적
    const proxyGet = new Proxy(get, {
      apply: (target, thisArg, args) => {
        const [atom] = args;
        dependencies.push(atom);
        return target(...args);
      },
    });

    // selector의 계산 로직 실행
    config.get(proxyGet);

    return dependencies;
  }
}

4-3. 메모이제이션 전략

function createMemoizedSelector(config) {
  let lastDependencies = null;
  let lastResult = null;

  return {
    read: (get) => {
      // 현재 의존성 추적
      const currentDependencies = trackDependencies(get);

      // 의존성 변경 여부 확인
      const dependenciesChanged =
        !lastDependencies ||
        currentDependencies.some(
          (dep, index) => dep !== lastDependencies[index]
        );

      // 캐시된 결과 또는 새로운 결과 반환
      if (!dependenciesChanged) {
        return lastResult;
      }

      // 새로운 결과 계산
      const newResult = config.get(get);

      // 상태 업데이트
      lastDependencies = currentDependencies;
      lastResult = newResult;

      return newResult;
    },
  };
}

4-4. 비동기 selector 처리

function asyncSelector({
    key,
    get: async (get) => {
        // 비동기 작업 처리
        // Recoil은 내부적으로 Suspense와 연동
        const dependencyValue = get(someAtom);
        const result = await fetchData(dependencyValue);
        return result;
    }
})

핵심 구현 전략

  • 프록시를 통한 정교한 의존성 추적
  • 세밀한 메모이제이션
  • 비동기 상태 관리의 복잡성 숨기기