TSyringe

TSyringe๋ž€

Syringe(์ฃผ์‚ฌ๊ธฐ) ์ฆ‰, ์ฃผ์‚ฌ๊ธฐ๋กœ ์ฃผ์ž…ํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.. ๋ฌด์—‡์„? ์˜์กด์„ฑ์„ MS์—์„œ ๋งŒ๋“  TypeScript์šฉ DI ๋„๊ตฌ(IoC Container)

TSyringe๋Š” ES6์˜ Reflect.metadata API์™€ TypeScript์˜ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์˜์กด์„ฑ ์ฃผ์ž…์„ ๊ตฌํ˜„

External Store๋ฅผ ๊ด€๋ฆฌํ•˜๋Š”๋ฐ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

ESbuild๋ฅผ ์‚ฌ์šฉํ•˜๋Š” Vite์—์„œ๋Š” decorator๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š์•„ Tsyringe๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜์—†๋‹ค.. #Issue

์˜์กด์„ฑ ์„ค์น˜

npm i tsyringe reflect-metadata

์ฝ”๋“œ ์ง„์ž…์ ์ด ๋˜๋Š” ํŒŒ์ผ src/main.tsx ์™€ src/setupTexts.ts(ํ…Œ์ŠคํŠธ)์—์„œ reflect-metadata ์ž„ํฌํŠธ

import 'reflect-metadata';

Signleton ์˜ˆ์ œ

์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๊ด€๋ฆฌํ•  CounterStore ํด๋ž˜์Šค ์ค€๋น„

import { singleton } from 'tsyringe';

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

์‹ฑ๊ธ€ํ†ค CounterStore ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉ

import { container } from 'tsyringe';

const counterStore = container.resolve(CounterStore);

Test Code

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);
    });
  });
});

TSyringe์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ฐ์ฒด๋ฅผ ์ดˆ๊ธฐํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค.

container.clearInstances();

singleton (์‹ฑ๊ธ€ํ†ค)

๊ฐ์ฒด์ง€ํ–ฅ ๋””์ž์ธ ํŒจํ„ด ์ค‘ ํ•˜๋‚˜๋กœ, ํŠน์ • ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ์˜ค์ง ํ•˜๋‚˜๋งŒ ์ƒ์„ฑ๋˜๋„๋ก ๋ณด์žฅํ•˜๋Š” ํŒจํ„ด

  • ์—ฌ๋Ÿฌ ๊ณณ์—์„œ ๋™์ผํ•œ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ณต์œ ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๊ณ ์ž ํ•  ๋•Œ ์œ ์šฉ

  • ์™ธ๋ถ€์—์„œ ์ธ์Šคํ„ด์Šค๋ฅผ ์ง์ ‘ ์ƒ์„ฑํ•˜์ง€ ๋ชปํ•˜๊ฒŒ ํด๋ž˜์Šค์˜ ์ƒ์„ฑ์ž๋ฅผ ๋ง‰์•„๋‘”๋‹ค.

  • ํด๋ž˜์Šค ๋‚ด๋ถ€์—์„œ ์œ ์ผํ•œ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ์ด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ •์  ๋ฉ”์†Œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ ์ œ๊ณต

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

    return SingletonClass.instance;
  }
}

์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์ธ ํด๋ž˜์Šค๊ฐ€ ๋‹ค๋ฅธ ํด๋ž˜์Šค์™€ ๊ฒฐํ•ฉ๋„๊ฐ€ ๋†’๋‹ค๋ฉด DI ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜์ž

Last updated