NestJS에서 **모듈(Module)**은 앱을 구성하는 기본 단위이자 코드의 구조를 분리하고 재사용성을 높이기 위한 핵심 개념이야.
Nest는 모듈 기반 아키텍처를 강제함으로써 의존성 주입(DI), 내부 경계 명확화, 기능 캡슐화를 쉽게 만들어줘.
NestJS에서 모듈은 관련된 컴포넌트(서비스, 컨트롤러, 프로바이더 등)를 그룹으로 묶는 단위야.
@Module({
imports: [], // 다른 모듈들
controllers: [], // 이 모듈이 가진 라우터들
providers: [], // 서비스/헬퍼 등 주입 가능한 클래스들
exports: [], // 외부 모듈에서 이 모듈의 어떤 provider를 쓸지
})
export class UserModule {}
Nest는 프로젝트를 기능 단위로 쪼개는 **도메인 기반 구조(Domain Driven Design)**를 권장하고,
모듈은 그 도메인을 캡슐화하는 단위야.
예를 들어:
UserModule → 회원 관련AuthModule → 인증 관련ProductModule → 상품 관련각 모듈은 자기 내부에서만 동작하는 서비스/컨트롤러/엔티티를 가질 수 있고,
필요한 기능만 exports/imports를 통해 주고받을 수 있어.
| 항목 | 설명 |
|---|---|
controllers | 이 모듈이 가진 HTTP 엔드포인트 |
providers | 의존성 주입 대상 (서비스, 유틸, 헬퍼 등) |
imports | 다른 모듈을 가져와 사용할 수 있음 |
exports | 이 모듈 외부에서 사용 가능한 provider만 공개함 |
// auth.module.ts
@Module({
providers: [AuthService],
exports: [AuthService], // 외부에서 사용 가능하게
})
export class AuthModule {}
// user.module.ts
@Module({
imports: [AuthModule], // AuthService를 쓰기 위해 import
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}
UserService가 AuthService를 DI 받을 수 있는 이유는:
AuthModule이 AuthService를 exports했고UserModule이 AuthModule을 imports 했기 때문이야.exports)imports)@Global()
@Module({ ... })
export class ConfigModule {}
NestJS는 **모듈 단위가 도메인(업무 영역)**이 되는 걸 가장 강하게 권장해.
즉, 하나의 모듈은 하나의 "주제", "업무 영역", 혹은 "비즈니스 기능"을 표현함.
예:
UserModule → 유저 등록, 조회, 탈퇴AuthModule → 로그인, 토큰 발급, 인증OrderModule → 주문 생성, 상태 변경ProductModule → 상품 등록, 수정이렇게 나누면 유지보수와 테스트가 쉬워지고, 의존 관계도 명확하게 가늠할 수 있어.
예:
"유저가 상품을 주문하면, 포인트 차감 + 재고 차감 + 주문 생성 + 알림 전송"
이건 여러 도메인의 모듈(User, Product, Order, Notification)이 얽힌 작업이지.
도메인 서비스들(UserService, OrderService 등)을 조합하는 ‘Application Layer 서비스’를 별도로 만든다.
// order-orchestrator.service.ts
@Injectable()
export class OrderOrchestratorService {
constructor(
private readonly userService: UserService,
private readonly productService: ProductService,
private readonly orderService: OrderService,
private readonly notificationService: NotificationService,
) {}
async placeOrder(userId: string, productId: string) {
const user = await this.userService.findById(userId);
const product = await this.productService.checkAvailability(productId);
await this.userService.deductPoints(user.id, product.price);
await this.productService.decreaseStock(product.id);
const order = await this.orderService.create(user.id, product.id);
await this.notificationService.sendOrderConfirmation(user.email);
return order;
}
}
Orchestrator, ApplicationService, UseCaseService 등으로 짓기도 해.ApplicationModule 생성@Module({
imports: [UserModule, ProductModule, OrderModule, NotificationModule],
providers: [OrderOrchestratorService],
})
export class ApplicationModule {}
이렇게 하면 도메인 간의 조합로직을 한 곳에 캡슐화할 수 있음.
작업의 중심이 되는 도메인이 명확할 경우, 해당 도메인 모듈 안에 넣기도 해.
예: 주문 흐름의 핵심이 주문(Order)이라면, OrderModule 안에 OrderService, OrderOrchestratorService 둘 다 넣는 식.
| 질문 | 답변 |
|---|---|
| 모듈 단위는 도메인 기준이 맞나? | ✅ NestJS에서 권장하는 구조이며 현실적인 기준임 |
| 도메인을 넘는 복잡한 작업은 어디서? | 여러 도메인 서비스를 **조합하는 Application Service (Orchestrator)**로 따로 구현 |
| 그건 어떤 모듈에 넣어야? | ① 별도 ApplicationModule, ② 중심 도메인 모듈 중 택일 가능 |
**“도메인을 얼마나 작게 쪼개야 하냐”**는 건 NestJS뿐 아니라 DDD(Domain-Driven Design), 모듈 아키텍처 설계 전반에서 계속 나오는 핵심 이슈인데,
정답은 하나가 아니라 **"업무 의미 단위에 따라 쪼갤 수 있을 만큼 쪼개되, 불필요하게 분리하지는 말라"**는 게 기준이야.
하나의 모듈은 하나의 '업무 목적'을 가져야 해.
너무 작게 나누면 복잡도만 늘어나고, 너무 크게 두면 응집도가 떨어져.
| 모듈 이름 | 괜찮은 분리? | 이유 |
|---|---|---|
UserModule | ✅ | "회원 등록/조회/탈퇴" 등 유저 관련 책임 묶음 |
AuthModule | ✅ | "로그인/토큰/인증"은 책임과 관심사가 다름 |
ProfileModule | ✅ | 유저의 개인정보 수정, 조회 등만 따로 관리 가능 |
UserCreateModule, UserDeleteModule | ❌ | 너무 세분화 → 오히려 관심사 중복 및 유지보수 어려움 |
회원, 인증, 주문, 배송 등의 용어가 각각 등장한다면 → 모듈 분리 대상| 기능 영역 | 모듈 예시 |
|---|---|
| 유저 관리 | UserModule |
| 인증 | AuthModule |
| 상품 | ProductModule |
| 주문 | OrderModule |
| 결제 | PaymentModule |
| 알림 | NotificationModule |
| 설정 | ConfigModule |
| 공통 유틸 | SharedModule, CommonModule |
feature 단위 서브모듈을 모듈 안에 폴더 수준으로 넣는 방식 추천| 질문 | 답변 |
|---|---|
| 도메인을 얼마나 작게 쪼개야 해? | 의미 있는 업무 단위로 쪼개되, 너무 작게 나누면 관리 지옥이 된다 |
| 기준은 뭐야? | 비즈니스 용어 / 책임 응집도 / 변경 시점 / 독립 배포 가능성 |
| Nest에서 실무적인 쪼갬은? | User, Auth, Order, Product 등 실제 도메인 중심으로 모듈 구성 |
NestJS의 모듈은 도메인 중심으로 적절히 작게 나누되,
여러 도메인 간 로직은 별도의 조합 서비스(Application Layer)에서 처리하고,
너무 큰 모듈은 테스트, 유지보수, 성능 측면에서 반드시 분리하는 게 좋다.