DI
의존성이란?
의존성이란 간단하게 두 모듈 간의 연결이다.
class A {
foo() { ... }
}
class B {
getList() {
const a = new A();
const data = a.foo();
...
}
}
- B클래스는 A라는 클래스를 사용해서 getList라는 메서드를 사용한다. 이 말은 B는 A에 의존한다고 할 수 있다.
의존성 주입이란?
메인 모듈이 ‘직접’ 다른 하위 모듈에 대한 의존성을 주기보다는 중간에 의존성 주입자가 이 부분을 가로채 메인 모듈이 ‘간접’적으로 의존성을 주입하는 방식이다. 모듈의 확장성과 독립성에 큰 도움을 주고 있다
- 이를 통해 메인 모듈(상위 모듈)은 하위 모듈에 대한 의존성이 떨어지게 된다. (decoupleing : 비동조화)
1. 의존성 주입의 장단점
1-1) 장점
- 모듈들을 쉽게 교체할 수 있는 구조가 되어 테스팅하기 쉽고, 마이그레이션 하기도 수월하다.
마이그레이션이란?
데이터를 한 위치에서 다른 위치로, 한 형식에서 다른 형식으로 또는 한 앱에서 다른 앱으로 이동하는 프로세스이다.
- 구현할 때 추상화 레이어를 넣고 이를 기반으로 구현체를 넣어 주기 때문에 애플리케이션 의존성 방향이 일관되고, 쉽게 추론할 수 있으며, 모듈 간의 관계가 명확해진다.
1-2) 단점
- 모듈들이 분리되므로 클래스 수가 늘어나 복잡성이 증가될 수 있으며, 약간의 런타임 페널티가 생기기도 한다.
2. 의존성 주입의 원칙
- 상위 모듈은 하위모듈에서 어떠한 것도 가져오지 않아야 한다.
- 둘 다 추상화에 의존해야 하며, 이때 추상화는 세부 사항에 의존하지 말아야 한다.
3. JavaScript 의존성 주입 모듈 예
3-1) 의존성 주입 전
사용자 추가하고, 사용자들의 리스트 조회
' 사용자 데이터에 의존 '
const User = require('./User');
const UsersRepository = require('./users-repository');
async function getUsers() {
return UsersRepository.findAll();
}
async function addUser(userData) {
const user = new User(userData);
return UsersRepository.addUser(user);
}
module.exports = {
getUsers,
addUser
}
- service가 특정 repository와 연결되어있어 다른 repository로 바꾸고 싶다면, 위의 코드를 싹 다 바꿔야 한다.(확장성이 떨어짐)
- 모듈의 테스트가 힘들어진다. getUser 메서드가 잘 작동하는지 확인하려면, usersRepository에 대한 가짜 객체를 만들어줘야 한다.
3-2) 의존성 주입 후
const UsersService = require('./users');
const assert = require('assert');
describe('Users service', () => {
it('gets users', async () => {
const users = [{
id: 1,
firstname: 'Joe',
lastname: 'Doe'
}];
const usersRepository = {
findAll: async () => {
return users
}
};
const usersService = new UsersService(usersRepository);
assert.deepEqual(await usersService.getUsers(), users);
});
});
- service와 repository를 decoupleing 하게 되면서 다른 repository로 언제든지 편하게 적용할 수 있다.
- 의존성을 주입할 때 개별 의존성을 하나하나 인자로 전달하는 것보다 객체로 감싸서 한 번에 주는 게 더 좋다.
- describe() : 여러 테스트 케이스를 묶어준다.
- it() : 테스트 케이스
3-3) TypeScript function을 사용한 의존성 주입 예
type UsersDependencies = {
usersRepository: UsersRepository
mailer: Mailer
logger: Logger
};
export const usersService = (dependencies: UsersDependencies) => {
const findAll = () => dependencies.usersRepository.findAll();
const addUser = user => {
await dependencies.usersRepository.addUser(user)
dependencies.logger.info(`User created: ${user}`)
await dependencies.mailer.sendConfirmationLink(user)
dependencies.logger.info(`Confirmation link sent: ${user}`)
};
return {
findAll,
addUser
};
}
const service = usersService({
usersRepository,
mailer,
logger
});
- function은 parameter로 의존성을 주입한다.
- 의존성들을 모두 미리 세팅해야 하기 때문에 복잡하지만( usersRepository, mailer, logger) 외부 라이브러리로 해결할 수 있다(Awilix, TypeDI, Inversify)
4. Java 의존성 주입 모듈 예
4-1) 의존성 주입 전
햄버거 레시피가 변화하게 되었을 때, 변화된 레시피에 따라서 요리사는 햄버거 만드는 방법을 수정
' 햄버거 가게 요리사는 햄버거 레시피에 의존 '
class BurgerChef {
private BurgerRecipe burgerRecipe;
public BurgerChef() {
burgerRecipe = new HamBurgerRecipe();
//burgerRecipe = new CheeseBurgerRecipe();
//burgerRecipe = new ChickenBurgerRecipe();
}
}
interface BurgerRecipe {
newBurger();
// 이외의 다양한 메소드
}
class HamBurgerRecipe implements BurgerRecipe {
public Burger newBurger() {
return new HamBerger();
}
// ...
}
- 의존관계를 인터페이스로 추상화하게 되면, 다양한 의존 관계 맺을 수 있다.
4-2) 의존성 주입 후
/* 생성자를 이용하는 방법 */
class BurgerChef {
private BurgerRecipe burgerRecipe;
public BurgerChef(BurgerRecipe burgerRecipe) {
this.burgerRecipe = burgerRecipe;
}
}
class BurgerRestaurantOwner {
private BurgerChef burgerChef = new BurgerChef(new HamburgerRecipe());
public void changeMenu() {
burgerChef = new BurgerChef(new CheeseBurgerRecipe());
}
}
- 생성자를 이용한 의존성 주입
/* 메서드를 이용하는 방법 (대표적으로 Setter 메서드) */
class BurgerChef {
private BurgerRecipe burgerRecipe = new HamburgerRecipe();
public void setBurgerRecipe(BurgerRecipe burgerRecipe) {
this.burgerRecipe = burgerRecipe;
}
}
class BurgerRestaurantOwner {
private BurgerChef burgerChef = new BurgerChef();
public void changeMenu() {
burgerChef.setBurgerRecipe(new CheeseBurgerRecipe());
}
}
- set 메서드를 이요한 의존성 주입
반응형
'CS' 카테고리의 다른 글
프로세스 메모리(스택, 힙, 데이터 영역, 코드 영역) 구조 개념 및 특징 (0) | 2023.01.13 |
---|---|
옵저버(Observer ) 패턴 개념 및 구현 방법(Java, JavaScript) (0) | 2023.01.10 |
알고리즘 BigO(시간 복잡도, 공간 복잡도) 개념 및 예제 (0) | 2023.01.05 |
팩토리(factory) 패턴 개념 및 JavaScript에서 사용 방법 (1) | 2023.01.04 |
싱글톤(Singleton) 패턴 개념 및 Node.js DB(MySQL, MongoDB) 연결 모듈 구현 (1) | 2022.12.26 |