[CS] DI, IoC, 그리고 ORM: 개발 생산성을 높이는 핵심 개념 완벽 정리
1. DI (Dependency Injection)
1) DI란?
• 정의
DI(Dependency Injection)는 객체가 필요한 의존성을 스스로 생성하지 않고,
외부에서 주입받는 디자인 패턴입니다.
• 작동 원리
DI는 IoC(Inversion of Control)의 한 형태로, 객체 생성 및 의존성 관리를
프레임워크(IoC 컨테이너)가 담당합니다.
(NestJS 예시)
NestJS에서는 @Injectable() 데코레이터로 클래스를 제공자로 등록하고,
이를 통해 의존성을 주입받습니다.
2) DI의 장점과 예시
• DI의 장점
- 결합도 감소
- 객체 간 결합도가 낮아져 코드 구조가 유연해집니다.
- 테스트 용이성
- Mock 객체를 활용해 단위 테스트를 간단하게 구성할 수 있습니다.
- 재사용성 증가
- 의존성을 외부에서 주입받기 때문에 코드가 더 쉽게 재사용됩니다.
3) Mock 객체란?
• 정의
소프트웨어 테스트에서 실제 객체의 동작을 모방하여, 테스트 환경을 구성하는 데
도움을 주는 객체입니다. 단위 테스트 시 의존성을 격리하거나 특정 동작/상태를
시뮬레이션하는 데 주로 활용됩니다.
• 특징
- 의존성 격리
- 테스트에서는 Mock 객체가 실제 의존성을 대체해, 테스트 대상 코드만 집중적으로 검증할 수 있습니다.
- 예측 가능한 동작
- 특정 메서드가 호출될 때 어떤 동작/값을 반환할지 사전에 설정이 가능합니다.
- 상태 검증
- 테스트 후 특정 메서드가 몇 번 호출되었는지 등도 확인할 수 있어, 테스트 정확도가 높아집니다.
4) DI 예시 (NestJS)
아래는 NestJS에서 DI를 사용하는 간단한 예시 코드입니다:
import { Injectable } from '@nestjs/common';
@Injectable()
export class CatsService {
private readonly cats: Cat[] = [];
findAll(): Cat[] {
return this.cats;
}
}
• CatsService에 @Injectable() 데코레이터를 붙이면, NestJS IoC 컨테이너가 이 클래스를 관리합니다.
5) DI의 다양한 형태 (NestJS 기준)
- 표준 제공자: 클래스 자체를 제공자로 등록
- 값 제공자 (useValue): 상수 값이나 특정 객체를 주입
- 클래스 제공자 (useClass): 팩토리 함수를 사용하여 동적으로 인스턴스 생성
- 별칭 제공자 (useExisting): 기존 제공자의 별칭을 통해 재사용
2. IoC (Inversion of Control)
1) IoC의 정의
IoC는 프로그램 흐름의 제어를 뒤바꾸는 개념입니다.
일반적으로 객체 생성 및 의존성 관리를 “프레임워크나 컨테이너”에 위임함으로써,
개발자가 아닌 프레임워크가 객체 생명주기를 관리하는 형태를 의미합니다.
2) DI와의 관계
DI는 IoC를 구현하는 한 가지 방식입니다.
(예: NestJS) IoC 컨테이너가 애플리케이션 내의 모든 객체(제공자) 생성과
의존성 해결을 담당합니다.
3) NestJS에서의 DI와 IoC
- 제공자 정의
- @Injectable() 데코레이터로 클래스를 NestJS IoC에 등록
- 의존성 요청
- Controller 등에서 생성자 주입으로 필요한 서비스를 요청
- 모듈 등록
- @Module() 데코레이터를 사용해 Controller와 Service 등을 묶어 모듈로 등록
예시:
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
@Module({
controllers: [CatsController],
providers: [CatsService],
})
export class AppModule {}
- CatsService가 AppModule에 등록되어, CatsController에서 생성자 주입을 통해 사용됩니다.
3. ORM (Object-Relational Mapping)
1) ORM의 정의
ORM은 객체 지향 프로그래밍 언어의 클래스(객체)와 관계형 데이터베이스 테이블을
자동으로 매핑해 줌으로써, 개발자가 직접 SQL을 작성하지 않고도 CRUD 등을
처리할 수 있게 해주는 기술입니다.
2) ORM의 작동 방식
- 객체와 데이터베이스 매핑
- 예: User 객체 ↔ users 테이블
- 쿼리 자동 생성
- 객체 상태 변화에 따라 ORM이 내부적으로 적절한 SQL 쿼리를 생성·실행
- 데이터 영속성
- ORM을 통해 객체가 DB 저장소에 영속(저장 및 재사용)되어, 프로그램 재시작 후에도 동일 상태로 작업을 이어갈 수 있습니다.
영속적이란?
프로그램이 종료되어도 데이터(객체 상태)가 DB 등 비휘발성 매체에 유지되는 것을 말합니다.
이로써 프로그램 재시작 후에도 이전 상태를 복구할 수 있습니다.
3) ORM의 장점과 단점
• 장점
- 개발 간소화 & 코드 양 감소
- 객체 조작만으로 DB 처리를 할 수 있어, SQL 작성 부담이 줄어듭니다.
- 유지보수성 높음
- 객체 지향적 로직과 DB 스키마 간 불일치가 줄어들어, 구조가 명확해집니다.
• 단점
- 추상화에 따른 내부 동작 파악 어려움
- 복잡한 쿼리 최적화가 필요한 상황 등에는 ORM 추상화가 걸림돌이 될 수도 있습니다.
- 잘못된 DB 설계 위험
- ORM만 믿고 DB 구조를 자동화하다 보면 비효율적인 스키마가 만들어질 가능성이 있습니다.
4. 결론
DI와 IoC로 애플리케이션 구조를 유연하고 테스트하기 쉽게 만들고, ORM으로 데이터베이스 조작을 단순화할 수 있습니다.
이들을 적절히 조합하면
•개발 속도는 빨라지고
•유지보수는 용이해지며
•비즈니스 로직에 좀 더 집중할 수 있게 됩니다.
NestJS와 같은 프레임워크에서 DI·IoC·ORM을 함께 활용해보세요!