2. TSyringe

학습 키워드

  • TSyringe

  • 의존성 주입(Dependency Injection)

  • reflect-metadata

  • singleton (싱글톤)

TSyringe (발음: 시린지)

영어 단어(syringe)의 뜻은 주사기라는 의미다. 앞의 T가 붙은 것은 TS와 연결짓는 말장난.

MS에서 만들었고, IoC 컨테이너의 역할을 한다. 의존성을 주입해준다. 테스트라던가, 미묘하게 조립해야 하는 경우에 많이 쓴다. External Store를 관리하는 데 사용할 것

React에서는 Props Drilling 이슈가 있다. 데이터가 단방향으로 (부모 -> 자식)으로 흐르기 때문에, 컴포넌트 구조의 뎁스가 깊은 경우 부모 컴포넌트의 상태를 자식 컴포넌트가 사용하려면 그 상태를 사용하지 않는 컴포넌트들도 props로 상태를 받아서 내려줘야 한다.

tsyringe와 reflect-metadata를 설치해준다.

npm i tsyringe reflect-metadata

상태는 Store라는 클래스를 만들어서 쓸 것.

// CounterStore.ts
import { singleton } from 'tsyringe';

type Listener = () => void;

@singleton()
export default class CounterStore {
  count = 0;

  listeners = new Set<Listener>();

  publish() {
    this.listeners.forEach((listener) => listener());
  }

  addListener(listener: Listener) {
    this.listeners.add(listener);
    this.publish();
  }

  removeListener(listener: Listener) {
    this.listeners.delete(listener);
    this.publish();
  }
}

// hooks/useCounterStore.tsx

import useForceUpdate from '../useForceUpdate';
import CounterStore from '../../stores/CounterStore';

export default function useCounterStore() {
  const counterStore = container.resolve(CounterStore);

  const forceUpdate = useForceUpdate();

  useEffect(() => {
    counterStore.addListener(forceUpdate);

    return () => {
      counterStore.removeListener(forceUpdate);
    };
  }, [counterStore, forceUpdate]);

  return counterStore;
}

// Counter.tsx
import { container } from 'tsyringe';

export default function Counter() {
  const counterStore = useCounterStore();

  const forceUpdate = useForceUpdate();

  useEffect(() => {
    counterStore.addListener(forceUpdate);

    return () => {
      counterStore.removeListener(forceUpdate);
    };
  }, [counterStore, forceUpdate]);

  const handleClickIncrease = () => {
    counterStore.increase();
  };

  const handleClickDecrease = () => {
    counterStore.decrease();
  };
}

리액트 App의 진입점인 main.tsx(or App.tsx)에서 reflect-metadata를 import해줘야 한다. 테스트를 위해서는 setupTests.ts 파일에 reflect-metadata를 import해준다.

@붙여서 쓰는 것을 데코레이터라고 하는데, 데코레이터를 쓰려면 tsconfig.json에 속성을 추가해줘야 한다.

  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }

tsyringe가 알아서 싱글톤으로 관리해준다.

테스트 코드를 작성할 때는 각 테스트 케이스마다 Store를 초기화해줘야한다.

beforeEach(() => {
  container.clearInstances();
});

Last updated