TSyringe

ν•™μŠ΅ν‚€μ›Œλ“œ

  • μ˜μ‘΄μ„± μ£Όμž…(Dependency Injection)

  • TSyringe

    • reflect-metadata

    • singleton (싱글톀)

μ˜μ‘΄μ„± μ£Όμž…(Dependency Injection)

  • ν•˜λ‚˜μ˜ 객체가 λ‹€λ₯Έ 객체의 μ˜μ‘΄μ„±μ„ μ œκ³΅ν•˜λŠ” ν…Œν¬λ‹‰

  • μ˜μ‘΄μ„± μ£Όμž…μ˜ μ˜λ„λŠ” 객체의 생성과 μ‚¬μš©μ˜ 관심을 λΆ„λ¦¬ν•˜λŠ” 것 β‡’ μ΄λŠ” 가독성과 μ½”λ“œ μž¬μ‚¬μš©μ„ λ†’ν˜€μ€€λ‹€.

예제λ₯Ό 톡해 이해해보기

  • TypeScript둜 API call을 ν•˜λŠ” 둜직

import axios from 'axios';

export const fetchTodo = (todoId: number) => {
  return axios.get<Todo>(`/todos`, { params: { todoId } });
};

fetchTodo ν•¨μˆ˜λŠ” axios λ₯Ό import ν•΄μ„œ μ΄μš©ν•œλ‹€. 즉, fetchTodo ν•¨μˆ˜λŠ” axios λͺ¨λ“ˆμ— μ˜μ‘΄ν•œλ‹€.

πŸ€” κ·Έλ ‡λ‹€λ©΄ μ˜μ‘΄μ„± μ£Όμž…μ€?

React의 Context API μ˜μ‘΄μ„± μ£Όμž… 도ꡬ

// @/contexts/MyContext.ts
export const MyContext = createContext<MyContextType>({});


// μ–΄λ”˜κ°€μ— μžˆλŠ” λΆ€λͺ¨ μ»΄ν¬λ„ŒνŠΈ
const Parent = () => {
  return (
    <MyContext.Provider value={{ age: 22 }}>
      ...
    </MyContext.Provider>
  );
};


// μ–΄λ”˜κ°€μ— μžˆλŠ” μžμ‹ μ»΄ν¬λ„ŒνŠΈ
const Child = () => {
  const { age } = useContext(MyContext);
  
  return <div>λ‚˜μ΄: {age}</div>;
}; 

Context APIλŠ” Provider λ₯Ό 톡해 값을 μ£Όμž…ν•΄ μ£Όλ©΄, useContext λ₯Ό 톡해 ν•΄λ‹Ή 값을 κ΅¬λ…ν•˜λŠ” ν˜•νƒœλ₯Ό κ°€μ§€κ²Œ λœλ‹€. Child λŠ” age λΌλŠ” 값을 import ν•˜μ§€ μ•Šμ•˜κ³ , Context API λ₯Ό 톡해 μ£Όμž…λ°›μ•˜λ‹€.

TSyringe

A lightweight dependency injection container for TypeScript/JavaScript for constructor injection.

  • TypeScript용 DI(μ˜μ‘΄μ„± μ£Όμž…) 도ꡬ

🧐 TSyringe ν•™μŠ΅ν•˜λŠ” μ΄μœ λŠ”?

TSyringeλ₯Ό μ‚¬μš©ν•΄μ„œ Prop Drilling문제λ₯Ό Contextλ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šκ³  TSyringeλ₯Ό μ‚¬μš©ν•œ ν•΄κ²° 방법을 ν•™μŠ΅

πŸ‘©πŸ»β€πŸ’» κ°„λ‹¨ν•œ μ‹€μŠ΅μ„ 톡해 TSyringe μ‚¬μš©ν•΄λ³΄κΈ°

1. βš™οΈ TSyringe, reflect-metadata μ„€μΉ˜

npm i tsyringe reflect-metadata

2. μ‹±κΈ€ν†€μœΌλ‘œ 관리할 CounterStore Classλ₯Ό μ€€λΉ„

mkdir src/stores
touch src/stores/CounterStore.ts
// src/stores/CounterStore.ts
import { singleton } from 'tsyringe';

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

3. πŸ“ src/main.tsx 와 src/setupTests.ts reflect-metadata import

🚨 tsyringe requires a reflect polyfill. Please add 'import "reflect-metadata"' to the top of your entry point.

β”œβ”€β”€ package.json
β”œβ”€β”€ package-lock.json
β”œβ”€β”€ src
β”‚   β”œβ”€β”€ main.tsx βœ…
β”‚   β”œβ”€β”€ App.tsx
β”‚   β”œβ”€β”€ App.test.tsx 
β”‚   β”œβ”€β”€ setupTests.ts βœ… 
β”‚   β”œβ”€β”€ hooks πŸ“
β”‚   β”‚   β”œβ”€β”€ useForceUpdate.ts 
β”‚   β”œβ”€β”€ components πŸ“
β”‚   β”‚   β”œβ”€β”€ Counter.tsx 
import 'reflect-metadata';

ν•΄λ‹Ή App의 μ‹œμž‘ 지점(μ§„μž…μ§€μ ) main.tsx reflect-metadataλ₯Ό importν•΄μ€˜μ•Ό ν•œλ‹€. ν…ŒμŠ€νŠΈλ₯Ό μœ„ν•΄μ„œλŠ” setupTests.ts νŒŒμΌμ— reflect-metadataλ₯Ό importν•΄μ€€λ‹€.

4. @ λ°μ½”λ ˆμ΄ν„°λ₯Ό μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„  tsconfig.json 파일 속성 μΆ”κ°€

"experimentalDecorators": true,
"emitDecoratorMetadata": true

5. container에 λ“±λ‘λ˜μ–΄μ„œ resolveλ₯Ό 톡해 CounterStore μ ‘κ·Ό (μ˜μ‘΄μ„± μ£Όμž…)

// scr/components/Counter.tsx

import { container } from 'tsyringe'; 

import useForceUpdate from '../hooks/useForceUpdate';

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

export default function Counter() {
  const store = container.resolve(CounterStore);
  const forceUpdate = useForceUpdate();

  const handleClick = () => {
    store.count += 1;
    forceUpdate();
  };

  return (
    <div>
      <p>{store.count}</p>
      <button type="button" onClick={handleClick}>
        Increase
      </button>
    </div>
  );
}

βœ… μ‹€μŠ΅μ„ 톡해 μ•Œκ²Œ 된 점

  • Counter/ CounterControl μ»΄ν¬λ„ŒνŠΈλ‘œ 뢄리 β‡’ CounterStore의 count 곡유 방법

    • Counter β†’ 화면에 좜λ ₯λ˜λŠ” 갯수

    • CounterControl β†’ Increase λ²„νŠΌ

  • Counter μ»΄ν¬λ„ŒνŠΈκ°€ 2개 경우 β‡’ count 값이 λ™μΌν•˜κ²Œ 적용 될 수 μžˆλ„λ‘ μ²˜λ¦¬ν•˜λŠ” 방법

    • new Set

    • useEffect (add, delete)

  • 관심사 뢄리 β‡’ External Store(CounterStore)을 ν™œμš©ν•΄ hooks으둜 λΆ„λ¦¬ν•΄μ„œ μž¬μ‚¬μš© ν•˜λŠ” 방법

singleton(싱글톀)

  • 단 ν•˜λ‚˜μ˜ μœ μΌν•œ 객체λ₯Ό λ§Œλ“€κΈ° μœ„ν•œ μ½”λ“œ νŒ¨ν„΄

  • λ©”λͺ¨λ¦¬ μ ˆμ•½μ„ μœ„ν•΄ μΈμŠ€ν„΄μŠ€κ°€ ν•„μš”ν•  λ•Œ λ˜‘κ°™μ€ μΈμŠ€ν„΄μŠ€λ₯Ό μƒˆλ‘œ λ§Œλ“€μ§€ μ•Šκ³  기쑴의 μΈμŠ€ν„΄μŠ€λ₯Ό 가져와 ν™œμš©ν•˜λŠ” 기법

  • λŒ€ν‘œμ μΈ μ˜ˆμ‹œ β†’ λ°μ΄ν„°λ² μ΄μŠ€ μ—°κ²° λͺ¨λ“ˆ

μš°λ¦¬κ°€ μ „μ—­ λ³€μˆ˜λΌλŠ”κ±Έ λ§Œλ“€μ–΄μ„œ μ‚¬μš©ν•˜λŠ” μ΄μœ λŠ” λ˜‘κ°™μ€ 데이터λ₯Ό λ©”μ„œλ“œλ§ˆλ‹€ 지역 λ³€μˆ˜λ‘œ μ„ μ–Έν•΄μ„œ μ‚¬μš©ν•˜λ©΄ μž¬μ‚¬μš©μ„± μΈ‘λ©΄μ—μ„œ λ‚­λΉ„, μ „μ—­μ—μ„œ ν•œλ²ˆλ§Œ 데이터λ₯Ό μ„ μ–Έν•˜κ³  가져와 μ‚¬μš©ν•˜λ©΄ 효율적이기 λ•Œλ¬Έμ΄λ‹€.

싱글톀 기법은 였직 ν•œκ°œμ˜ μΈμŠ€ν„΄μŠ€ 생성을 λ³΄μ¦ν•˜μ—¬ νš¨μœ¨μ„ 찾을 수 μžˆμ§€λ§Œ, λ¬Έμ œμ λ“€μ΄ 수반되기 λ•Œλ¬Έμ— κ· ν˜•μž‘νžŒ 선택을 ν•˜λŠ” 것이 μ€‘μš”ν•˜λ‹€. κ·Έλž˜μ„œ 직접 μœ μ €κ°€ λ§Œλ“€μ–΄ μ‚¬μš©ν•˜λŠ” κ²ƒλ³΄λ‹€λŠ” ν”„λ ˆμž„μ›Œν¬μ˜ 도움을 λ°›μ•„ μ‚¬μš©ν•΄μ„œ λ¬Έμ œμ λ“€μ„ λ³΄μ•ˆν•˜λ©΄μ„œ μž₯점의 ν˜œνƒμ„ λˆ„λ¦΄ 수 μžˆλ‹€. (λ‚΄λΆ€μ μœΌλ‘œ 클래슀의 μ œμ–΄λ₯Ό IoC(Inversion of Control)λ°©μ‹μ˜ μ»¨ν…Œμ΄λ„ˆμ—κ²Œ λ„˜κ²¨ κ΄€λ¦¬ν•˜κΈ° λ•Œλ¬Έμ—)

πŸ”— μ°Έκ³ 

Last updated