# TSyringe

## TSyringe란

> Syringe(주사기) 즉, 주사기로 **주입하는 역할**을 한다.. 무엇을? 의존성을\
> MS에서 만든 TypeScript용 DI 도구(IoC Container)

TSyringe는 ES6의 `Reflect.metadata` API와 TypeScript의 데코레이터를 활용하여 의존성 주입을 구현

&#x20;External Store를 관리하는데 활용할 수 있다.

{% hint style="info" %}
ESbuild를 사용하는 Vite에서는 decorator를 지원하지 않아 Tsyringe를 사용할 수없다.. [#Issue](https://github.com/vitejs/vite/issues/788)
{% endhint %}

### 의존성 설치&#x20;

```bash
npm i tsyringe reflect-metadata
```

코드 진입점이 되는 파일 `src/main.tsx` 와 `src/setupTexts.ts(테스트)`에서 **reflect-metadata** 임포트

```typescript
import 'reflect-metadata';
```

## Signleton 예제

싱글톤으로 관리할 CounterStore 클래스 준비

```typescript
import { singleton } from 'tsyringe';

@singleton()
class CounterStore {
  //...
}
```

싱글톤 CounterStore 객체를 사용

```typescript
import { container } from 'tsyringe';

const counterStore = container.resolve(CounterStore);
```

#### Test Code

```tsx
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { container } from 'tsyringe';

import App from './App';

const context = describe;

describe('App', () => {
  function renderApp() {
    return render(<App />);
  }

  beforeEach(() => {
    container.clearInstances();
  });
  
  it('render correctly', () => {
    renderApp();

    expect(screen.getByText('Getting Started'));
  });

  context('when increase button is clicked', () => {
    it('count is incremented by 1', async () => {
      renderApp();

      const increaseBtn = screen.getByRole('button', { name: 'Increase' });

      await userEvent.click(increaseBtn);
      await userEvent.click(increaseBtn);

      expect(screen.getAllByText(/counter: 2/).length).toBe(2);
    });
  });

  context('when decrease button is clicked', () => {
    it('count is decrease by 1', async () => {
      renderApp();

      const decreaseBtn = screen.getByRole('button', { name: 'Decrease' });

      await userEvent.click(decreaseBtn);
      await userEvent.click(decreaseBtn);

      expect(screen.getAllByText(/counter: -2/).length).toBe(2);
    });
  });
});
```

{% hint style="info" %}
TSyringe에서 관리하는 객체를 초기화할 수 있다.

```typescript
container.clearInstances();
```

{% endhint %}

## singleton (싱글톤)

> 객체지향 디자인 패턴 중 하나로, 특정 클래스의 인스턴스가 오직 하나만 생성되도록 보장하는 패턴

* 여러 곳에서 동일한 인스턴스를 공유하여 사용하고자 할 때 유용
* 외부에서 인스턴스를 직접 생성하지 못하게 클래스의 생성자를 막아둔다.
* 클래스 내부에서 유일한 인스턴스를 생성하고, 이를 반환하는 정적 메소드를 구현하여 제공

```tsx
class SingletonClass {
  private static instance: SingletonClass | null = null;
  
  static getInstance() {
    if (!this.instance) {
      SingletonClass.instance = new SingletonClass();
    }

    return SingletonClass.instance;
  }
}
```

{% hint style="info" %}
싱글톤 패턴인 클래스가 다른 클래스와 결합도가 높다면 DI 패턴을 사용하자
{% endhint %}
