Lsiron
Nest.js에서 모듈과 의존성 주입 이해하기 본문
1. AppModule에서 MoviesModule로 분리하기
app.module.ts
import { Module } from '@nestjs/common';
import { MoviesController } from './movies/movies.controller';
import { MoviesService } from './movies/movies.service';
@Module({
imports: [],
controllers: [MoviesController],
providers: [MoviesService],
})
export class AppModule {}
기존 우리의 app.module.ts 이다.
사실 이 app.module.ts는 AppController와 AppProvider만 가지고 있어야한다.
Nest.js는 모듈 기반 구조를 지향하기 때문에 AppModule은 보통 전체 애플리케이션을 구성하는 기본 모듈 역할을 하며, 각 도메인별로 별도의 모듈로 분리하는 것이 좋다.
따라서 MoviesController와 MoviesService를 새로운 MoviesModule로 옮기겠다.
먼저 아래의 명령어를 입력하여 movies.module.ts 파일을 생성해보자.
$ nest g mo movies
movies.module.ts 파일이 잘 만들어 진 것을 확인할 수 있다.
이제 MoviesModule이 생성되었으니, app.module.ts에서 MoviesController와 MoviesService를 삭제하고, 대신 MoviesModule을 import 해보자.
import { Module } from '@nestjs/common';
import { MoviesModule } from './movies/movies.module';
@Module({
imports: [MoviesModule],
controllers: [],
providers: [],
})
export class AppModule {}
다음으로 새로 생성된 movies.module.ts 파일로 가서 MoviesController와 MoviesService를 추가해보자.
import { Module } from '@nestjs/common';
import { MoviesController } from './movies.controller';
import { MoviesService } from './movies.service';
@Module({
imports: [],
controllers: [MoviesController],
providers: [MoviesService],
})
export class MoviesModule {}
이제 MoviesModule이 독립적으로 존재하게 되었으며, AppModule은 전체 애플리케이션의 기본 모듈 역할을 한다.
2. AppController 만들기
그럼 app.module.ts 파일의 AppController와 AppService는 무슨 용도로 사용할까?
AppController와 AppService는 주로 애플리케이션의 기본 URL(루트 경로, 예: /)을 처리하는 역할을 한다.
예를 들어, 애플리케이션이 실행되었을 때, 사용자가 메인 페이지 또는 기본 정보를 제공받는 진입점이 필요할 수 있다.
이 경우, AppController 와 AppService는 홈 페이지, 또는 서비스 상태와 같은 정보를 제공하는 데 사용된다.
허나, 만약 특정한 전역적인 엔드포인트가 필요하지 않다면 생략해도 괜찮다.
이제 애플리케이션의 기본 라우트를 처리하는 AppController를 만들어 보자.
아래 명령어를 입력하여 app.controller.ts 파일을 생성해 보자.
$ nest g co app
위와 같이 app 폴더가 생겼고 하위 파일들이 생겼다.
생성된 app.controller.ts 파일을 루트 디렉토리로 옮기고, app 폴더는 삭제 해 주자.
app.controller.ts 파일로 가서 아래와 같이 입력 해 주자.
import { Controller, Get } from '@nestjs/common';
@Controller('')
export class AppController {
@Get()
home() {
return 'Welcome to my Movie API';
}
}
이제 app.module.ts 파일로 가서 AppModule에서 AppController를 임포트하고 등록 해 보자.
import { Module } from '@nestjs/common';
import { MoviesModule } from './movies/movies.module';
import { AppController } from './app.controller';
@Module({
imports: [MoviesModule],
controllers: [AppController],
providers: [],
})
export class AppModule {}
이렇게 하면, 애플리케이션이 실행될 때 모든 모듈이 하나의 큰 모듈로 통합되어 작동한다.
비유를 통해 정리를 한번 해 보자.
1) AppModule은 회사의 전체 구조이다.
AppModule은 회사 전체 구조에 해당한다. AppModule은 빌딩 내에 어떤 부서들이 있는지(즉, 모듈들), 그 부서들이 어떤 자원(서비스 등)을 사용할지 관리한다.
즉, AppModule 안에서 각 부서와 서비스가 어떻게 연결되고, 협력할지를 결정하는 것이다.
예를 들어, MoviesModule이라는 영화 부서를 포함하고, 그 부서가 필요한 서비스(MoviesService)를 할당해주는 역할이다.
2) AppController는 리셉션이다.
리셉션은 회사에 처음 방문한 사람들에게 기본적인 안내를 제공한다.
즉, 리셉션은 고객을 맞이하고, 필요한 정보를 알려주거나 다른 부서로 연결해주는 역할을 한다.
이는 AppController와 같다. AppController는 애플리케이션의 기본 엔드포인트를 처리하며, API에 접근하는 사람들에게 간단한 정보를 제공하거나 첫 경로를 안내하는 역할을 한다.
3) AppService는 리셉션 매니저이다.
리셉션에 사람이 왔을 때, 안내 데스크에서 사람들을 맞이하고 정보를 제공하는데 필요한 내부 작업을 처리하는 사람이 바로 리셉션 매니저이다.
이 매니저는 리셉션의 실질적인 업무를 수행하며, 예를 들어, 회사의 운영 정보나 주요 안내 사항들을 관리하고 처리한다.
4) MovieModule은 영화 부서 전체이다.
MovieModule은 이 영화 부서 전체를 의미한다. MovieModule은 부서 내부의 구조와 어떤 팀(서비스)들이 영화와 관련된 작업을 처리할지를 관리한다.
5) MovieController는 영화 부서의 접수 창구이다.
MovieController는 접수 창구와 같은 역할을 한다. 클라이언트가 영화 데이터를 요청하거나 수정하려고 할 때, 이 요청을 받아서 처리할 담당자를 찾는 역할을 한다.
6) MovieService는 영화 부서의 실제 작업팀이다.
영화 부서 내에는 실제 작업을 수행하는 팀이 있다. 이 팀은 영화 데이터를 관리하고, 새로운 영화를 추가하거나, 영화를 삭제하고 업데이트하는 등의 일을 처리한다. 사람들이 접수 창구(Controller)를 통해 요청을 전달하면, 실제 일을 수행하는 팀이 해당 요청을 처리한다.
3. 의존성 주입(Dependency Injection)
하나 알아두어야 할 것은 바로 Nest.js에는 dependency injection 이라 부르는게 있다.
Nest.js의 핵심 개념 중 하나는 의존성 주입이다. 이를 통해 클래스 간의 의존성을 효율적으로 관리할 수 있다.
movies.controller.ts 파일로 가보자.
import { Controller, Get, Post, Delete, Patch, Param, Body } from '@nestjs/common';
import { MoviesService } from './movies.service';
import { Movie } from './entities/movie.entity';
import { CreateMovieDto } from './dto/create-movie.dto';
import { UpdateMovieDto } from './dto/update-movie.dto';
@Controller('movies')
export class MoviesController {
constructor(private readonly moviesService: MoviesService) {}
@Get()
getAll(): Movie[] {
return this.moviesService.getAll();
}
@Get('/:id')
getOne(@Param('id') movieId: number): Movie {
console.log(typeof movieId)
return this.moviesService.getOne(movieId);
}
@Post()
create(@Body() movieData: CreateMovieDto) {
return this.moviesService.create(movieData);
}
@Delete('/:id')
remove(@Param('id') movieId: number) {
return this.moviesService.delete(movieId);
}
@Patch('/:id')
patch(@Param('id') movieId: number, @Body() updateData: UpdateMovieDto) {
return this.moviesService.update(movieId, updateData)
}
}
위 코드에서 MoviesController는 MoviesService를 의존성으로 주입받고 있으며, 이 과정은 constructor 내부에서 발생한다.
아래의 movies.module.ts 파일을 보면 MoviesService는 MoviesModule의 providers 배열에 포함되어 있기 때문에 Nest.js는 이 서비스를 인스턴스화하고 MoviesController에 주입한다.
import { Module } from '@nestjs/common';
import { MoviesController } from './movies.controller';
import { MoviesService } from './movies.service';
@Module({
imports: [],
controllers: [MoviesController],
providers: [MoviesService],
})
export class MoviesModule {}
이를 바로 dependency injection 이라고 부른다.
movies.service.ts 파일로 가보자.
import { Injectable, NotFoundException } from '@nestjs/common';
import { Movie } from './entities/movie.entity';
import { CreateMovieDto } from './dto/create-movie.dto';
import { UpdateMovieDto } from './dto/update-movie.dto';
@Injectable()
export class MoviesService {
private movies: Movie[] = [];
getAll(): Movie[] {
return this.movies;
}
getOne(id:number): Movie {
const movie = this.movies.find(movie => movie.id === id);
if(!movie) {
throw new NotFoundException(`Movie with ID ${id} not found`)
}
return movie;
}
delete(id: number) {
this.getOne(id)
this.movies = this.movies.filter(movie => movie.id !== id);
}
create(movieData: CreateMovieDto){
this.movies.push({
id: this.movies.length + 1,
...movieData
})
}
update(id: number, updateData: UpdateMovieDto) {
const movie = this.getOne(id);
this.delete(id);
this.movies.push({...movie, ...updateData })
}
}
중요한 부분은 @Injectable() 데코레이터이다.
이처럼 MoviesService에 @Injectable() 데코레이터가 적용되어 있어, NestJS는 해당 클래스를 주입 가능한 서비스로 인식하고 다른 클래스에 의존성으로 주입할 수 있다. 이것이 바로 의존성 주입의 핵심 원리이다.
참조: 노마드코더
'백엔드 > Nest.js' 카테고리의 다른 글
Nest.js에서 유닛테스팅 해보기 (4) | 2024.08.28 |
---|---|
Nest.js에서 Jest로 유닛 테스트와 E2E 테스트 시작하기 (5) | 2024.08.28 |
Nest.js에서 DTO를 통해 유효성 검사 처리하기 (0) | 2024.08.23 |
Nest.js에서 Controller와 Service 다루기 (0) | 2024.08.22 |
Nest.js 에서 API 만들기 (get, post, patch, delete) (0) | 2024.08.22 |