의존성 주입(Dependency Injection, DI)이란?
객체가 다른 객체를 직접 생성하거나 관리하지 않고, 객체 간의 의존성을 외부에서 주입하는 방식으로, 객체 간의 의존성을 낮추는 중요한 설계 원칙 중 하나이다.
장점
유연성: 객체 간의 결합도가 낮아져서, 각각의 객체를 독립적으로 변경하거나 교체할 수 있다.
테스트 용이성: 테스트 시 실제 객체 대신 Mock 객체를 주입하여 단위 테스트를 쉽게 수행할 수 있다.
재사용성: 각 객체가 독립적이므로, 다른 곳에서 재사용하기 쉽다.
기본 개념 이해하기
객체가 다른 객체에 의존하고 있는 상태. 예를 들어, 서비스(Service)가 특정 데이터베이스(DB)에 의존하는 경우를 생각할 수 있다.
필요한 객체를 외부에서 주입하는 행위. 주입 방법은 여러 가지가 있다(생성자 주입, 세터 주입, 인터페이스 주입 등).
예시: 의존성 주입이 없는 경우
프로젝트의 구조가 아래와 같을 때,
Repository: 데이터베이스와 직접 상호작용합니다.
Service: 비즈니스 로직을 담당합니다.
Controller: HTTP 요청을 처리합니다.
Router: 라우팅을 담당합니다.
class UserRepository {
constructor() {
// 직접 Prisma 인스턴스를 생성
this.prisma = new PrismaClient();
}
findAll() {
return this.prisma.user.findMany();
}
}
class UserService {
constructor() {
// 직접 UserRepository 인스턴스를 생성
this.userRepository = new UserRepository();
}
getAllUsers() {
return this.userRepository.findAll();
}
}
class UserController {
constructor() {
// 직접 UserService 인스턴스를 생성
this.userService = new UserService();
}
async getUsers(req, res) {
const users = await this.userService.getAllUsers();
res.json(users);
}
}
여기서 문제는 각 클래스가 직접 다른 클래스를 생성하여 의존성을 가지고 있다는 점이다. 이런 구조에서는 각 클래스가 강하게 결합되어 있어, 변경이나 테스트가 어렵다.
예시: 의존성 주입을 사용한 경우
class UserRepository {
constructor(prisma) {
this.prisma = prisma;
}
findAll() {
return this.prisma.user.findMany();
}
}
class UserService {
constructor(userRepository) {
this.userRepository = userRepository;
}
getAllUsers() {
return this.userRepository.findAll();
}
}
class UserController {
constructor(userService) {
this.userService = userService;
}
async getUsers(req, res) {
const users = await this.userService.getAllUsers();
res.json(users);
}
}
import express from 'express';
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
const userRepository = new UserRepository(prisma);
const userService = new UserService(userRepository);
const userController = new UserController(userService);
const router = express.Router();
router.get('/users', (req, res) => userController.getUsers(req, res));
export default router;